updated ZTO version
This commit is contained in:
@@ -27,7 +27,7 @@ INCLUDES=
|
|||||||
DEFS=
|
DEFS=
|
||||||
ARCH_FLAGS=-arch x86_64
|
ARCH_FLAGS=-arch x86_64
|
||||||
CFLAGS=
|
CFLAGS=
|
||||||
CXXFLAGS=$(CFLAGS) -fno-rtti
|
CXXFLAGS=$(CFLAGS) -fno-rtti -std=c++11
|
||||||
|
|
||||||
include objects.mk
|
include objects.mk
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ INCLUDES=
|
|||||||
DEFS=
|
DEFS=
|
||||||
ARCH_FLAGS=-arch x86_64
|
ARCH_FLAGS=-arch x86_64
|
||||||
CFLAGS=
|
CFLAGS=
|
||||||
CXXFLAGS=$(CFLAGS) -fno-rtti
|
CXXFLAGS=$(CFLAGS) -fno-rtti -std=c++11
|
||||||
|
|
||||||
include objects.mk
|
include objects.mk
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ INCLUDES+= -Iext \
|
|||||||
# --------------------------------- ZT Config ----------------------------------
|
# --------------------------------- ZT Config ----------------------------------
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
ZTFLAGS:=-DSDK -DZT_ONE_NO_ROOT_CHECK
|
ZTFLAGS:=-DZT_SDK -DZT_ONE_NO_ROOT_CHECK
|
||||||
|
|
||||||
# Disable codesign since open source users will not have ZeroTier's certs
|
# Disable codesign since open source users will not have ZeroTier's certs
|
||||||
CODESIGN=echo
|
CODESIGN=echo
|
||||||
|
|||||||
16
objects.mk
16
objects.mk
@@ -1,14 +1,15 @@
|
|||||||
OBJS=\
|
OBJS=\
|
||||||
zerotierone/ext/json-parser/json.o \
|
zerotierone/controller/EmbeddedNetworkController.o \
|
||||||
zerotierone/ext/http-parser/http_parser.o \
|
zerotierone/controller/JSONDB.o \
|
||||||
zerotierone/ext/lz4/lz4.o \
|
|
||||||
zerotierone/node/C25519.o \
|
zerotierone/node/C25519.o \
|
||||||
|
zerotierone/node/Capability.o \
|
||||||
zerotierone/node/CertificateOfMembership.o \
|
zerotierone/node/CertificateOfMembership.o \
|
||||||
|
zerotierone/node/CertificateOfOwnership.o \
|
||||||
zerotierone/node/Cluster.o \
|
zerotierone/node/Cluster.o \
|
||||||
zerotierone/node/DeferredPackets.o \
|
|
||||||
zerotierone/node/Identity.o \
|
zerotierone/node/Identity.o \
|
||||||
zerotierone/node/IncomingPacket.o \
|
zerotierone/node/IncomingPacket.o \
|
||||||
zerotierone/node/InetAddress.o \
|
zerotierone/node/InetAddress.o \
|
||||||
|
zerotierone/node/Membership.o \
|
||||||
zerotierone/node/Multicaster.o \
|
zerotierone/node/Multicaster.o \
|
||||||
zerotierone/node/Network.o \
|
zerotierone/node/Network.o \
|
||||||
zerotierone/node/NetworkConfig.o \
|
zerotierone/node/NetworkConfig.o \
|
||||||
@@ -18,15 +19,18 @@ OBJS=\
|
|||||||
zerotierone/node/Path.o \
|
zerotierone/node/Path.o \
|
||||||
zerotierone/node/Peer.o \
|
zerotierone/node/Peer.o \
|
||||||
zerotierone/node/Poly1305.o \
|
zerotierone/node/Poly1305.o \
|
||||||
|
zerotierone/node/Revocation.o \
|
||||||
zerotierone/node/Salsa20.o \
|
zerotierone/node/Salsa20.o \
|
||||||
zerotierone/node/SelfAwareness.o \
|
zerotierone/node/SelfAwareness.o \
|
||||||
zerotierone/node/SHA512.o \
|
zerotierone/node/SHA512.o \
|
||||||
zerotierone/node/Switch.o \
|
zerotierone/node/Switch.o \
|
||||||
|
zerotierone/node/Tag.o \
|
||||||
zerotierone/node/Topology.o \
|
zerotierone/node/Topology.o \
|
||||||
zerotierone/node/Utils.o \
|
zerotierone/node/Utils.o \
|
||||||
zerotierone/osdep/BackgroundResolver.o \
|
|
||||||
zerotierone/osdep/ManagedRoute.o \
|
zerotierone/osdep/ManagedRoute.o \
|
||||||
zerotierone/osdep/Http.o \
|
zerotierone/osdep/Http.o \
|
||||||
zerotierone/osdep/OSUtils.o \
|
zerotierone/osdep/OSUtils.o \
|
||||||
zerotierone/service/ClusterGeoIpService.o \
|
zerotierone/service/ClusterGeoIpService.o \
|
||||||
zerotierone/service/ControlPlane.o
|
zerotierone/service/ControlPlane.o \
|
||||||
|
zerotierone/service/SoftwareUpdater.o \
|
||||||
|
zerotierone/ext/http-parser/http_parser.o
|
||||||
|
|||||||
@@ -25,13 +25,13 @@
|
|||||||
|
|
||||||
## Third-Party Code
|
## Third-Party Code
|
||||||
|
|
||||||
These are included in ext/ for platforms that do not have them available in common repositories. Otherwise they may be linked and the package may ship with them as dependencies.
|
ZeroTier includes the following third party code, either in ext/ or incorporated into the ZeroTier core.
|
||||||
|
|
||||||
* LZ4 compression algorithm by Yann Collet
|
* LZ4 compression algorithm by Yann Collet
|
||||||
|
|
||||||
* Files: ext/lz4/*
|
* Files: node/Packet.cpp (bundled within anonymous namespace)
|
||||||
* Home page: http://code.google.com/p/lz4/
|
* Home page: http://code.google.com/p/lz4/
|
||||||
* License grant: BSD attribution
|
* License grant: BSD 2-clause
|
||||||
|
|
||||||
* http-parser by Joyent, Inc. (many authors)
|
* http-parser by Joyent, Inc. (many authors)
|
||||||
|
|
||||||
@@ -39,11 +39,11 @@ These are included in ext/ for platforms that do not have them available in comm
|
|||||||
* Home page: https://github.com/joyent/http-parser/
|
* Home page: https://github.com/joyent/http-parser/
|
||||||
* License grant: MIT/Expat
|
* License grant: MIT/Expat
|
||||||
|
|
||||||
* json-parser by James McLaughlin
|
* C++11 json (nlohmann/json) by Niels Lohmann
|
||||||
|
|
||||||
* Files: ext/json-parser/*
|
* Files: ext/json/*
|
||||||
* Home page: https://github.com/udp/json-parser/
|
* Home page: https://github.com/nlohmann/json
|
||||||
* License grant: BSD attribution
|
* License grant: MIT
|
||||||
|
|
||||||
* TunTapOSX by Mattias Nissler
|
* TunTapOSX by Mattias Nissler
|
||||||
|
|
||||||
@@ -55,26 +55,19 @@ These are included in ext/ for platforms that do not have them available in comm
|
|||||||
* tap-windows6 by the OpenVPN project
|
* tap-windows6 by the OpenVPN project
|
||||||
|
|
||||||
* Files: windows/TapDriver6/*
|
* Files: windows/TapDriver6/*
|
||||||
* Home page:
|
* Home page: https://github.com/OpenVPN/tap-windows6/
|
||||||
https://github.com/OpenVPN/tap-windows6/
|
|
||||||
* License grant: GNU GPL v2
|
* License grant: GNU GPL v2
|
||||||
* ZeroTier Modifications: change name of driver to ZeroTier, add ioctl() to get L2 multicast memberships (source is in ext/ and modifications inherit GPL)
|
* ZeroTier Modifications: change name of driver to ZeroTier, add ioctl() to get L2 multicast memberships (source is in ext/ and modifications inherit GPL)
|
||||||
|
|
||||||
* Salsa20 stream cipher, Curve25519 elliptic curve cipher, Ed25519
|
* Salsa20 stream cipher, Curve25519 elliptic curve cipher, Ed25519 digital signature algorithm, and Poly1305 MAC algorithm, all by Daniel J. Bernstein
|
||||||
digital signature algorithm, and Poly1305 MAC algorithm, all by
|
|
||||||
Daniel J. Bernstein
|
|
||||||
|
|
||||||
* Files:
|
* Files: node/Salsa20.* node/C25519.* node/Poly1305.*
|
||||||
node/Salsa20.hpp
|
|
||||||
node/C25519.hpp
|
|
||||||
node/Poly1305.hpp
|
|
||||||
* Home page: http://cr.yp.to/
|
* Home page: http://cr.yp.to/
|
||||||
* License grant: public domain
|
* License grant: public domain
|
||||||
|
* ZeroTier Modifications: slight cryptographically-irrelevant modifications for inclusion into ZeroTier core
|
||||||
|
|
||||||
* MiniUPNPC and libnatpmp by Thomas Bernard
|
* MiniUPNPC and libnatpmp by Thomas Bernard
|
||||||
|
|
||||||
* Files:
|
* Files: ext/libnatpmp/* ext/miniupnpc/*
|
||||||
ext/libnatpmp/*
|
|
||||||
ext/miniupnpc/*
|
|
||||||
* Home page: http://miniupnp.free.fr/
|
* Home page: http://miniupnp.free.fr/
|
||||||
* License grant: BSD attribution no-endorsement
|
* License grant: BSD attribution no-endorsement
|
||||||
|
|||||||
@@ -11,8 +11,14 @@ ifeq ($(OSTYPE),Linux)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(OSTYPE),FreeBSD)
|
ifeq ($(OSTYPE),FreeBSD)
|
||||||
include make-freebsd.mk
|
CC=gcc
|
||||||
|
CXX=g++
|
||||||
|
ZT_BUILD_PLATFORM=7
|
||||||
|
include make-bsd.mk
|
||||||
endif
|
endif
|
||||||
ifeq ($(OSTYPE),OpenBSD)
|
ifeq ($(OSTYPE),OpenBSD)
|
||||||
include make-freebsd.mk
|
CC=egcc
|
||||||
|
CXX=eg++
|
||||||
|
ZT_BUILD_PLATFORM=9
|
||||||
|
include make-bsd.mk
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
ZeroTier - A Planetary Ethernet Switch
|
ZeroTier - A Planetary Ethernet Switch
|
||||||
======
|
======
|
||||||
|
|
||||||
ZeroTier is a software-based managed Ethernet switch for planet Earth.
|
ZeroTier is an advanced SDN Ethernet switch for planet Earth. It erases the LAN/WAN distinction and makes VPNs, tunnels, proxies, and other kludges arising from the inflexible nature of physical networks obsolete. Everything is encrypted end-to-end and traffic takes the most direct (peer to peer) path available.
|
||||||
|
|
||||||
It erases the LAN/WAN distinction and makes VPNs, tunnels, proxies, and other kludges arising from the inflexible nature of physical networks obsolete. Everything is encrypted end-to-end and traffic takes the most direct (peer to peer) path available.
|
|
||||||
|
|
||||||
This repository contains ZeroTier One, a service that provides ZeroTier network connectivity to devices running Windows, Mac, Linux, iOS, Android, and FreeBSD and makes joining virtual networks as easy as joining IRC or Slack channels. It also contains the OS-independent core ZeroTier protocol implementation in [node/](node/).
|
|
||||||
|
|
||||||
Visit [ZeroTier's site](https://www.zerotier.com/) for more information and [pre-built binary packages](https://www.zerotier.com/download.shtml). Apps for Android and iOS are available for free in the Google Play and Apple app stores.
|
Visit [ZeroTier's site](https://www.zerotier.com/) for more information and [pre-built binary packages](https://www.zerotier.com/download.shtml). Apps for Android and iOS are available for free in the Google Play and Apple app stores.
|
||||||
|
|
||||||
@@ -13,40 +9,66 @@ Visit [ZeroTier's site](https://www.zerotier.com/) for more information and [pre
|
|||||||
|
|
||||||
ZeroTier's basic operation is easy to understand. Devices have 10-digit *ZeroTier addresses* like `89e92ceee5` and networks have 16-digit network IDs like `8056c2e21c000001`. All it takes for a device to join a network is its 16-digit ID, and all it takes for a network to authorize a device is its 10-digit address. Everything else is automatic.
|
ZeroTier's basic operation is easy to understand. Devices have 10-digit *ZeroTier addresses* like `89e92ceee5` and networks have 16-digit network IDs like `8056c2e21c000001`. All it takes for a device to join a network is its 16-digit ID, and all it takes for a network to authorize a device is its 10-digit address. Everything else is automatic.
|
||||||
|
|
||||||
A "device" can be anything really: desktops, laptops, phones, servers, VMs/VPSes, containers, and even (soon) apps.
|
A "device" in our terminology is any "unit of compute" capable of talking to a network: desktops, laptops, phones, servers, VMs/VPSes, containers, and even user-space applications via our [SDK](https://github.com/zerotier/ZeroTierSDK).
|
||||||
|
|
||||||
For testing we provide a public virtual network called *Earth* with network ID `8056c2e21c000001`. On Linux and Mac you can do this with:
|
For testing purposes we provide a public virtual network called *Earth* with network ID `8056c2e21c000001`. You can join it with:
|
||||||
|
|
||||||
sudo zerotier-cli join 8056c2e21c000001
|
sudo zerotier-cli join 8056c2e21c000001
|
||||||
|
|
||||||
Now wait about 30 seconds and check your system with `ip addr list` or `ifconfig`. You'll see a new interface whose name starts with *zt* and it should quickly get an IPv4 and an IPv6 address. Once you see it get an IP, try pinging `earth.zerotier.net` at `29.209.112.93`. If you've joined Earth from more than one system, try pinging your other machine.
|
Now wait about 30 seconds and check your system with `ip addr list` or `ifconfig`. You'll see a new interface whose name starts with *zt* and it should quickly get an IPv4 and an IPv6 address. Once you see it get an IP, try pinging `earth.zerotier.net` at `29.209.112.93`. If you've joined Earth from more than one system, try pinging your other machine. If you don't want to belong to a giant Ethernet party line anymore, just type:
|
||||||
|
|
||||||
*(IPv4 addresses for Earth are assigned from the block 28.0.0.0/7, which is not a part of the public Internet but is non-standard for private networks. It's used to avoid IP conflicts during testing. Your networks can run any IP addressing scheme you want.)*
|
|
||||||
|
|
||||||
If you don't want to belong to a giant Ethernet party line anymore, just type:
|
|
||||||
|
|
||||||
sudo zerotier-cli leave 8056c2e21c000001
|
sudo zerotier-cli leave 8056c2e21c000001
|
||||||
|
|
||||||
The *zt* interface will disappear. You're no longer on the network.
|
The *zt* interface will disappear. You're no longer on the network.
|
||||||
|
|
||||||
To create networks of your own you'll need a network controller. You can use [our hosted controller at my.zerotier.com](https://my.zerotier.com) which is free for up to 100 devices on an unlimited number of networks, or you can build your own controller and run it through its local JSON API. See [README.md in controller/](controller/) for more information.
|
To create networks of your own, you'll need a network controller. ZeroTier One (for desktops and servers) includes controller functionality in its default build that can be configured via its JSON API (see [README.md in controller/](controller/)). ZeroTier provides a hosted solution with a nice web UI and SaaS add-ons at [my.zerotier.com](https://my.zerotier.com/). Basic controller functionality is free for up to 100 devices.
|
||||||
|
|
||||||
### Building from Source
|
### Project Layout
|
||||||
|
|
||||||
For Mac, Linux, and BSD, just type "make" (or "gmake" on BSD). You won't need much installed; here are the requirements for various platforms:
|
- `artwork/`: icons, logos, etc.
|
||||||
|
- `attic/`: old stuff and experimental code that we want to keep around for reference.
|
||||||
|
- `controller/`: the reference network controller implementation, which is built and included by default on desktop and server build targets.
|
||||||
|
- `debian/`: files for building Debian packages on Linux.
|
||||||
|
- `doc/`: manual pages and other documentation.
|
||||||
|
- `ext/`: third party libraries, binaries that we ship for convenience on some platforms (Mac and Windows), and installation support files.
|
||||||
|
- `include/`: include files for the ZeroTier core.
|
||||||
|
- `java/`: a JNI wrapper used with our Android mobile app. (The whole Android app is not open source but may be made so in the future.)
|
||||||
|
- `macui/`: a Macintosh menu-bar app for controlling ZeroTier One, written in Objective C.
|
||||||
|
- `node/`: the ZeroTier virtual Ethernet switch core, which is designed to be entirely separate from the rest of the code and able to be built as a stand-alone OS-independent library. Note to developers: do not use C++11 features in here, since we want this to build on old embedded platforms that lack C++11 support. C++11 can be used elsewhere.
|
||||||
|
- `osdep/`: code to support and integrate with OSes, including platform-specific stuff only built for certain targets.
|
||||||
|
- `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.
|
||||||
|
- `tcp-proxy/`: TCP proxy code run by ZeroTier, Inc. to provide TCP fallback (this will die soon!).
|
||||||
|
- `windows/`: Visual Studio solution files, Windows service code for ZeroTier One, and the Windows task bar app UI.
|
||||||
|
|
||||||
* **Mac**: Xcode command line tools. It should build on OSX 10.7 or newer.
|
The base path contains the ZeroTier One service main entry point (`one.cpp`), self test code, makefiles, etc.
|
||||||
* **Linux**: gcc/g++ (4.9 or newer recommended) or clang/clang++ (3.4 or newer recommended) Makefile will use clang by default if available. The Linux build will auto-detect the presence of development headers for *json-parser*, *http-parser*, *li8bnatpmp*, and *libminiupnpc* and will link against the system libraries for these if they are present and recent enough. Otherwise the bundled versions in [ext/](ext/) will be used. Type `make install` to install the binaries and other files on the system, though this will not create init.d or systemd links.
|
|
||||||
* **FreeBSD**: C++ compiler (G++ usually) and GNU make (gmake).
|
|
||||||
|
|
||||||
Each supported platform has its own *make-XXX.mk* file that contains the actual make rules for the platform. The right .mk file is included by the main Makefile based on the GNU make *OSTYPE* variable. Take a look at the .mk file for your platform for other targets, debug build rules, etc.
|
### 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.7 or newer are required.
|
||||||
|
- Tap device driver kext source is in `ext/tap-mac` and a signed pre-built binary can be found in `ext/bin/tap-mac`. You should not need to build it yourself. It's a fork of [tuntaposx](http://tuntaposx.sourceforge.net) with device names changed to `zt#`, support for a larger MTU, and tun functionality removed.
|
||||||
|
- **Linux**
|
||||||
|
- The minimum compiler versions required are GCC/G++ 4.9.3 or CLANG/CLANG++ 3.4.2.
|
||||||
|
- 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.
|
||||||
|
- CentOS 7 ships with a version of GCC/G++ that is too old, but a new enough version of CLANG can be found in the *epel* repositories. Type `yum install epel-release` and then `yum install clang` to build there.
|
||||||
|
- **Windows**
|
||||||
|
- Windows 7 or newer (and equivalent server versions) are supported. This *may* work on Vista but you're on your own there. Windows XP is not supported since it lacks many important network API functions.
|
||||||
|
- We build with Visual Studio 2015. Older versions may not work with the solution file and project files we ship and may not have new enough C++11 support.
|
||||||
|
- Pre-built signed Windows drivers are included in `ext/bin/tap-windows-ndis6`. The MSI files found there will install them on 32-bit and 64-bit systems. (These are included in our multi-architecture installer as chained MSIs.)
|
||||||
|
- Windows builds are more painful in general than other platforms and are for the adventurous.
|
||||||
|
- **FreeBSD**
|
||||||
|
- Tested most recently on FreeBSD-11. Older versions may work but we're not sure.
|
||||||
|
- GCC/G++ 4.9 and gmake are required. These can be installed from packages or ports. Type `gmake` to build.
|
||||||
|
- **OpenBSD**
|
||||||
|
- There is a limit of four network memberships on OpenBSD as there are only four tap devices (`/dev/tap0` through `/dev/tap3`). We're not sure if this can be increased.
|
||||||
|
- OpenBSD lacks `getifmaddrs` (or any equivalent method) to get interface multicast memberships. As a result multicast will only work on OpenBSD for ARP and NDP (IP/MAC lookup) and not for other purposes.
|
||||||
|
- Only tested on OpenBSD 6.0. Older versions may not work.
|
||||||
|
- GCC/G++ 4.9 and gmake are required and can be installed using `pkg_add` or from ports. They get installed in `/usr/local/bin` as `egcc` and `eg++` and our makefile is pre-configured to use them on OpenBSD.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
Windows, of course, is special. We build for Windows with Microsoft Visual Studio 2012 on Windows 7. A solution file is located in the *windows/* subfolder. Newer versions of Visual Studio (and Windows) may work but haven't been tested. Older versions almost certainly will not, since they lack things like *stdint.h* and certain STL features. MinGW or other ports of gcc/clang to Windows should also work but haven't been tested.
|
|
||||||
|
|
||||||
32 and 64 bit X86 and ARM (e.g. Raspberry Pi, Android) are officially supported. Community members have built for MIPS and Sparc without issues.
|
|
||||||
|
|
||||||
### Running
|
### Running
|
||||||
|
|
||||||
Running *zerotier-one* with -h will show help.
|
Running *zerotier-one* with -h will show help.
|
||||||
@@ -62,7 +84,7 @@ The service is controlled via the JSON API, which by default is available at 127
|
|||||||
Here's where home folders live (by default) on each OS:
|
Here's where home folders live (by default) on each OS:
|
||||||
|
|
||||||
* **Linux**: `/var/lib/zerotier-one`
|
* **Linux**: `/var/lib/zerotier-one`
|
||||||
* **FreeBSD**: `/var/db/zerotier-one`
|
* **FreeBSD** / **OpenBSD**: `/var/db/zerotier-one`
|
||||||
* **Mac**: `/Library/Application Support/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 for Windows 7. The base 'shared app data' folder might be different on different Windows versions.)
|
||||||
|
|
||||||
|
|||||||
@@ -1366,12 +1366,7 @@ reexecute:
|
|||||||
|| c != CONTENT_LENGTH[parser->index]) {
|
|| c != CONTENT_LENGTH[parser->index]) {
|
||||||
parser->header_state = h_general;
|
parser->header_state = h_general;
|
||||||
} else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
|
} else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
|
||||||
if (parser->flags & F_CONTENTLENGTH) {
|
|
||||||
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
parser->header_state = h_content_length;
|
parser->header_state = h_content_length;
|
||||||
parser->flags |= F_CONTENTLENGTH;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1474,6 +1469,12 @@ reexecute:
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parser->flags & F_CONTENTLENGTH) {
|
||||||
|
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
parser->flags |= F_CONTENTLENGTH;
|
||||||
parser->content_length = ch - '0';
|
parser->content_length = ch - '0';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ extern "C" {
|
|||||||
/* Also update SONAME in the Makefile whenever you change these. */
|
/* Also update SONAME in the Makefile whenever you change these. */
|
||||||
#define HTTP_PARSER_VERSION_MAJOR 2
|
#define HTTP_PARSER_VERSION_MAJOR 2
|
||||||
#define HTTP_PARSER_VERSION_MINOR 7
|
#define HTTP_PARSER_VERSION_MINOR 7
|
||||||
#define HTTP_PARSER_VERSION_PATCH 0
|
#define HTTP_PARSER_VERSION_PATCH 1
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#if defined(_WIN32) && !defined(__MINGW32__) && \
|
#if defined(_WIN32) && !defined(__MINGW32__) && \
|
||||||
@@ -90,6 +90,76 @@ typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
|||||||
typedef int (*http_cb) (http_parser*);
|
typedef int (*http_cb) (http_parser*);
|
||||||
|
|
||||||
|
|
||||||
|
/* Status Codes */
|
||||||
|
#define HTTP_STATUS_MAP(XX) \
|
||||||
|
XX(100, CONTINUE, Continue) \
|
||||||
|
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
|
||||||
|
XX(102, PROCESSING, Processing) \
|
||||||
|
XX(200, OK, OK) \
|
||||||
|
XX(201, CREATED, Created) \
|
||||||
|
XX(202, ACCEPTED, Accepted) \
|
||||||
|
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
|
||||||
|
XX(204, NO_CONTENT, No Content) \
|
||||||
|
XX(205, RESET_CONTENT, Reset Content) \
|
||||||
|
XX(206, PARTIAL_CONTENT, Partial Content) \
|
||||||
|
XX(207, MULTI_STATUS, Multi-Status) \
|
||||||
|
XX(208, ALREADY_REPORTED, Already Reported) \
|
||||||
|
XX(226, IM_USED, IM Used) \
|
||||||
|
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
|
||||||
|
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
|
||||||
|
XX(302, FOUND, Found) \
|
||||||
|
XX(303, SEE_OTHER, See Other) \
|
||||||
|
XX(304, NOT_MODIFIED, Not Modified) \
|
||||||
|
XX(305, USE_PROXY, Use Proxy) \
|
||||||
|
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
|
||||||
|
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
|
||||||
|
XX(400, BAD_REQUEST, Bad Request) \
|
||||||
|
XX(401, UNAUTHORIZED, Unauthorized) \
|
||||||
|
XX(402, PAYMENT_REQUIRED, Payment Required) \
|
||||||
|
XX(403, FORBIDDEN, Forbidden) \
|
||||||
|
XX(404, NOT_FOUND, Not Found) \
|
||||||
|
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
|
||||||
|
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
|
||||||
|
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
|
||||||
|
XX(408, REQUEST_TIMEOUT, Request Timeout) \
|
||||||
|
XX(409, CONFLICT, Conflict) \
|
||||||
|
XX(410, GONE, Gone) \
|
||||||
|
XX(411, LENGTH_REQUIRED, Length Required) \
|
||||||
|
XX(412, PRECONDITION_FAILED, Precondition Failed) \
|
||||||
|
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
|
||||||
|
XX(414, URI_TOO_LONG, URI Too Long) \
|
||||||
|
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
|
||||||
|
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
|
||||||
|
XX(417, EXPECTATION_FAILED, Expectation Failed) \
|
||||||
|
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
|
||||||
|
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
|
||||||
|
XX(423, LOCKED, Locked) \
|
||||||
|
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
|
||||||
|
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
|
||||||
|
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
|
||||||
|
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
|
||||||
|
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
|
||||||
|
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
|
||||||
|
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
|
||||||
|
XX(501, NOT_IMPLEMENTED, Not Implemented) \
|
||||||
|
XX(502, BAD_GATEWAY, Bad Gateway) \
|
||||||
|
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
|
||||||
|
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
|
||||||
|
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
|
||||||
|
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
|
||||||
|
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
|
||||||
|
XX(508, LOOP_DETECTED, Loop Detected) \
|
||||||
|
XX(510, NOT_EXTENDED, Not Extended) \
|
||||||
|
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
|
||||||
|
|
||||||
|
enum http_status
|
||||||
|
{
|
||||||
|
#define XX(num, name, string) HTTP_STATUS_##name = num,
|
||||||
|
HTTP_STATUS_MAP(XX)
|
||||||
|
#undef XX
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Request Methods */
|
/* Request Methods */
|
||||||
#define HTTP_METHOD_MAP(XX) \
|
#define HTTP_METHOD_MAP(XX) \
|
||||||
XX(0, DELETE, DELETE) \
|
XX(0, DELETE, DELETE) \
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
Copyright (C) 2012, 2013 James McLaughlin et al. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
SUCH DAMAGE.
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,283 +0,0 @@
|
|||||||
|
|
||||||
/* vim: set et ts=3 sw=3 sts=3 ft=c:
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved.
|
|
||||||
* https://github.com/udp/json-parser
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _JSON_H
|
|
||||||
#define _JSON_H
|
|
||||||
|
|
||||||
#ifndef json_char
|
|
||||||
#define json_char char
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef json_int_t
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <inttypes.h>
|
|
||||||
#define json_int_t int64_t
|
|
||||||
#else
|
|
||||||
#define json_int_t __int64
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
unsigned long max_memory;
|
|
||||||
int settings;
|
|
||||||
|
|
||||||
/* Custom allocator support (leave null to use malloc/free)
|
|
||||||
*/
|
|
||||||
|
|
||||||
void * (* mem_alloc) (size_t, int zero, void * user_data);
|
|
||||||
void (* mem_free) (void *, void * user_data);
|
|
||||||
|
|
||||||
void * user_data; /* will be passed to mem_alloc and mem_free */
|
|
||||||
|
|
||||||
size_t value_extra; /* how much extra space to allocate for values? */
|
|
||||||
|
|
||||||
} json_settings;
|
|
||||||
|
|
||||||
#define json_enable_comments 0x01
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
json_none,
|
|
||||||
json_object,
|
|
||||||
json_array,
|
|
||||||
json_integer,
|
|
||||||
json_double,
|
|
||||||
json_string,
|
|
||||||
json_boolean,
|
|
||||||
json_null
|
|
||||||
|
|
||||||
} json_type;
|
|
||||||
|
|
||||||
extern const struct _json_value json_value_none;
|
|
||||||
|
|
||||||
typedef struct _json_object_entry
|
|
||||||
{
|
|
||||||
json_char * name;
|
|
||||||
unsigned int name_length;
|
|
||||||
|
|
||||||
struct _json_value * value;
|
|
||||||
|
|
||||||
} json_object_entry;
|
|
||||||
|
|
||||||
typedef struct _json_value
|
|
||||||
{
|
|
||||||
struct _json_value * parent;
|
|
||||||
|
|
||||||
json_type type;
|
|
||||||
|
|
||||||
union
|
|
||||||
{
|
|
||||||
int boolean;
|
|
||||||
json_int_t integer;
|
|
||||||
double dbl;
|
|
||||||
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
unsigned int length;
|
|
||||||
json_char * ptr; /* null terminated */
|
|
||||||
|
|
||||||
} string;
|
|
||||||
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
unsigned int length;
|
|
||||||
|
|
||||||
json_object_entry * values;
|
|
||||||
|
|
||||||
#if defined(__cplusplus) && __cplusplus >= 201103L
|
|
||||||
decltype(values) begin () const
|
|
||||||
{ return values;
|
|
||||||
}
|
|
||||||
decltype(values) end () const
|
|
||||||
{ return values + length;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} object;
|
|
||||||
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
unsigned int length;
|
|
||||||
struct _json_value ** values;
|
|
||||||
|
|
||||||
#if defined(__cplusplus) && __cplusplus >= 201103L
|
|
||||||
decltype(values) begin () const
|
|
||||||
{ return values;
|
|
||||||
}
|
|
||||||
decltype(values) end () const
|
|
||||||
{ return values + length;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} array;
|
|
||||||
|
|
||||||
} u;
|
|
||||||
|
|
||||||
union
|
|
||||||
{
|
|
||||||
struct _json_value * next_alloc;
|
|
||||||
void * object_mem;
|
|
||||||
|
|
||||||
} _reserved;
|
|
||||||
|
|
||||||
#ifdef JSON_TRACK_SOURCE
|
|
||||||
|
|
||||||
/* Location of the value in the source JSON
|
|
||||||
*/
|
|
||||||
unsigned int line, col;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Some C++ operator sugar */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
inline _json_value ()
|
|
||||||
{ memset (this, 0, sizeof (_json_value));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const struct _json_value &operator [] (int index) const
|
|
||||||
{
|
|
||||||
if (type != json_array || index < 0
|
|
||||||
|| ((unsigned int) index) >= u.array.length)
|
|
||||||
{
|
|
||||||
return json_value_none;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *u.array.values [index];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const struct _json_value &operator [] (const char * index) const
|
|
||||||
{
|
|
||||||
if (type != json_object)
|
|
||||||
return json_value_none;
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < u.object.length; ++ i)
|
|
||||||
if (!strcmp (u.object.values [i].name, index))
|
|
||||||
return *u.object.values [i].value;
|
|
||||||
|
|
||||||
return json_value_none;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline operator const char * () const
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case json_string:
|
|
||||||
return u.string.ptr;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline operator json_int_t () const
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case json_integer:
|
|
||||||
return u.integer;
|
|
||||||
|
|
||||||
case json_double:
|
|
||||||
return (json_int_t) u.dbl;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline operator bool () const
|
|
||||||
{
|
|
||||||
if (type != json_boolean)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return u.boolean != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline operator double () const
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case json_integer:
|
|
||||||
return (double) u.integer;
|
|
||||||
|
|
||||||
case json_double:
|
|
||||||
return u.dbl;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} json_value;
|
|
||||||
|
|
||||||
json_value * json_parse (const json_char * json,
|
|
||||||
size_t length);
|
|
||||||
|
|
||||||
#define json_error_max 128
|
|
||||||
json_value * json_parse_ex (json_settings * settings,
|
|
||||||
const json_char * json,
|
|
||||||
size_t length,
|
|
||||||
char * error);
|
|
||||||
|
|
||||||
void json_value_free (json_value *);
|
|
||||||
|
|
||||||
|
|
||||||
/* Not usually necessary, unless you used a custom mem_alloc and now want to
|
|
||||||
* use a custom mem_free.
|
|
||||||
*/
|
|
||||||
void json_value_free_ex (json_settings * settings,
|
|
||||||
json_value *);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,360 +0,0 @@
|
|||||||
/*
|
|
||||||
LZ4 - Fast LZ compression algorithm
|
|
||||||
Header File
|
|
||||||
Copyright (C) 2011-2015, Yann Collet.
|
|
||||||
|
|
||||||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
You can contact the author at :
|
|
||||||
- LZ4 source repository : https://github.com/Cyan4973/lz4
|
|
||||||
- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#if defined (__cplusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* lz4.h provides block compression functions, and gives full buffer control to programmer.
|
|
||||||
* If you need to generate inter-operable compressed data (respecting LZ4 frame specification),
|
|
||||||
* and can let the library handle its own memory, please use lz4frame.h instead.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**************************************
|
|
||||||
* Version
|
|
||||||
**************************************/
|
|
||||||
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
|
|
||||||
#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */
|
|
||||||
#define LZ4_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */
|
|
||||||
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
|
|
||||||
int LZ4_versionNumber (void);
|
|
||||||
|
|
||||||
/**************************************
|
|
||||||
* Tuning parameter
|
|
||||||
**************************************/
|
|
||||||
/*
|
|
||||||
* LZ4_MEMORY_USAGE :
|
|
||||||
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
|
|
||||||
* Increasing memory usage improves compression ratio
|
|
||||||
* Reduced memory usage can improve speed, due to cache effect
|
|
||||||
* Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
|
|
||||||
*/
|
|
||||||
#define LZ4_MEMORY_USAGE 14
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************
|
|
||||||
* Simple Functions
|
|
||||||
**************************************/
|
|
||||||
|
|
||||||
int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
|
|
||||||
int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize);
|
|
||||||
|
|
||||||
/*
|
|
||||||
LZ4_compress_default() :
|
|
||||||
Compresses 'sourceSize' bytes from buffer 'source'
|
|
||||||
into already allocated 'dest' buffer of size 'maxDestSize'.
|
|
||||||
Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize).
|
|
||||||
It also runs faster, so it's a recommended setting.
|
|
||||||
If the function cannot compress 'source' into a more limited 'dest' budget,
|
|
||||||
compression stops *immediately*, and the function result is zero.
|
|
||||||
As a consequence, 'dest' content is not valid.
|
|
||||||
This function never writes outside 'dest' buffer, nor read outside 'source' buffer.
|
|
||||||
sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE
|
|
||||||
maxDestSize : full or partial size of buffer 'dest' (which must be already allocated)
|
|
||||||
return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize)
|
|
||||||
or 0 if compression fails
|
|
||||||
|
|
||||||
LZ4_decompress_safe() :
|
|
||||||
compressedSize : is the precise full size of the compressed block.
|
|
||||||
maxDecompressedSize : is the size of destination buffer, which must be already allocated.
|
|
||||||
return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize)
|
|
||||||
If destination buffer is not large enough, decoding will stop and output an error code (<0).
|
|
||||||
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
|
||||||
This function is protected against buffer overflow exploits, including malicious data packets.
|
|
||||||
It never writes outside output buffer, nor reads outside input buffer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************
|
|
||||||
* Advanced Functions
|
|
||||||
**************************************/
|
|
||||||
#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
|
|
||||||
#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
|
|
||||||
|
|
||||||
/*
|
|
||||||
LZ4_compressBound() :
|
|
||||||
Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
|
|
||||||
This function is primarily useful for memory allocation purposes (destination buffer size).
|
|
||||||
Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
|
|
||||||
Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize)
|
|
||||||
inputSize : max supported value is LZ4_MAX_INPUT_SIZE
|
|
||||||
return : maximum output size in a "worst case" scenario
|
|
||||||
or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE)
|
|
||||||
*/
|
|
||||||
int LZ4_compressBound(int inputSize);
|
|
||||||
|
|
||||||
/*
|
|
||||||
LZ4_compress_fast() :
|
|
||||||
Same as LZ4_compress_default(), but allows to select an "acceleration" factor.
|
|
||||||
The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
|
|
||||||
It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
|
|
||||||
An acceleration value of "1" is the same as regular LZ4_compress_default()
|
|
||||||
Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1.
|
|
||||||
*/
|
|
||||||
int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
LZ4_compress_fast_extState() :
|
|
||||||
Same compression function, just using an externally allocated memory space to store compression state.
|
|
||||||
Use LZ4_sizeofState() to know how much memory must be allocated,
|
|
||||||
and allocate it on 8-bytes boundaries (using malloc() typically).
|
|
||||||
Then, provide it as 'void* state' to compression function.
|
|
||||||
*/
|
|
||||||
int LZ4_sizeofState(void);
|
|
||||||
int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
LZ4_compress_destSize() :
|
|
||||||
Reverse the logic, by compressing as much data as possible from 'source' buffer
|
|
||||||
into already allocated buffer 'dest' of size 'targetDestSize'.
|
|
||||||
This function either compresses the entire 'source' content into 'dest' if it's large enough,
|
|
||||||
or fill 'dest' buffer completely with as much data as possible from 'source'.
|
|
||||||
*sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'.
|
|
||||||
New value is necessarily <= old value.
|
|
||||||
return : Nb bytes written into 'dest' (necessarily <= targetDestSize)
|
|
||||||
or 0 if compression fails
|
|
||||||
*/
|
|
||||||
int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
LZ4_decompress_fast() :
|
|
||||||
originalSize : is the original and therefore uncompressed size
|
|
||||||
return : the number of bytes read from the source buffer (in other words, the compressed size)
|
|
||||||
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
|
||||||
Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes.
|
|
||||||
note : This function fully respect memory boundaries for properly formed compressed data.
|
|
||||||
It is a bit faster than LZ4_decompress_safe().
|
|
||||||
However, it does not provide any protection against intentionally modified data stream (malicious input).
|
|
||||||
Use this function in trusted environment only (data to decode comes from a trusted source).
|
|
||||||
*/
|
|
||||||
int LZ4_decompress_fast (const char* source, char* dest, int originalSize);
|
|
||||||
|
|
||||||
/*
|
|
||||||
LZ4_decompress_safe_partial() :
|
|
||||||
This function decompress a compressed block of size 'compressedSize' at position 'source'
|
|
||||||
into destination buffer 'dest' of size 'maxDecompressedSize'.
|
|
||||||
The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached,
|
|
||||||
reducing decompression time.
|
|
||||||
return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize)
|
|
||||||
Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.
|
|
||||||
Always control how many bytes were decoded.
|
|
||||||
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
|
||||||
This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets
|
|
||||||
*/
|
|
||||||
int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize);
|
|
||||||
|
|
||||||
|
|
||||||
/***********************************************
|
|
||||||
* Streaming Compression Functions
|
|
||||||
***********************************************/
|
|
||||||
#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
|
|
||||||
#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long))
|
|
||||||
/*
|
|
||||||
* LZ4_stream_t
|
|
||||||
* information structure to track an LZ4 stream.
|
|
||||||
* important : init this structure content before first use !
|
|
||||||
* note : only allocated directly the structure if you are statically linking LZ4
|
|
||||||
* If you are using liblz4 as a DLL, please use below construction methods instead.
|
|
||||||
*/
|
|
||||||
typedef struct { long long table[LZ4_STREAMSIZE_U64]; } LZ4_stream_t;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* LZ4_resetStream
|
|
||||||
* Use this function to init an allocated LZ4_stream_t structure
|
|
||||||
*/
|
|
||||||
void LZ4_resetStream (LZ4_stream_t* streamPtr);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* LZ4_createStream will allocate and initialize an LZ4_stream_t structure
|
|
||||||
* LZ4_freeStream releases its memory.
|
|
||||||
* In the context of a DLL (liblz4), please use these methods rather than the static struct.
|
|
||||||
* They are more future proof, in case of a change of LZ4_stream_t size.
|
|
||||||
*/
|
|
||||||
LZ4_stream_t* LZ4_createStream(void);
|
|
||||||
int LZ4_freeStream (LZ4_stream_t* streamPtr);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* LZ4_loadDict
|
|
||||||
* Use this function to load a static dictionary into LZ4_stream.
|
|
||||||
* Any previous data will be forgotten, only 'dictionary' will remain in memory.
|
|
||||||
* Loading a size of 0 is allowed.
|
|
||||||
* Return : dictionary size, in bytes (necessarily <= 64 KB)
|
|
||||||
*/
|
|
||||||
int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* LZ4_compress_fast_continue
|
|
||||||
* Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio.
|
|
||||||
* Important : Previous data blocks are assumed to still be present and unmodified !
|
|
||||||
* 'dst' buffer must be already allocated.
|
|
||||||
* If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
|
|
||||||
* If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero.
|
|
||||||
*/
|
|
||||||
int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* LZ4_saveDict
|
|
||||||
* If previously compressed data block is not guaranteed to remain available at its memory location
|
|
||||||
* save it into a safer place (char* safeBuffer)
|
|
||||||
* Note : you don't need to call LZ4_loadDict() afterwards,
|
|
||||||
* dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue()
|
|
||||||
* Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error
|
|
||||||
*/
|
|
||||||
int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize);
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************
|
|
||||||
* Streaming Decompression Functions
|
|
||||||
************************************************/
|
|
||||||
|
|
||||||
#define LZ4_STREAMDECODESIZE_U64 4
|
|
||||||
#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
|
|
||||||
typedef struct { unsigned long long table[LZ4_STREAMDECODESIZE_U64]; } LZ4_streamDecode_t;
|
|
||||||
/*
|
|
||||||
* LZ4_streamDecode_t
|
|
||||||
* information structure to track an LZ4 stream.
|
|
||||||
* init this structure content using LZ4_setStreamDecode or memset() before first use !
|
|
||||||
*
|
|
||||||
* In the context of a DLL (liblz4) please prefer usage of construction methods below.
|
|
||||||
* They are more future proof, in case of a change of LZ4_streamDecode_t size in the future.
|
|
||||||
* LZ4_createStreamDecode will allocate and initialize an LZ4_streamDecode_t structure
|
|
||||||
* LZ4_freeStreamDecode releases its memory.
|
|
||||||
*/
|
|
||||||
LZ4_streamDecode_t* LZ4_createStreamDecode(void);
|
|
||||||
int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* LZ4_setStreamDecode
|
|
||||||
* Use this function to instruct where to find the dictionary.
|
|
||||||
* Setting a size of 0 is allowed (same effect as reset).
|
|
||||||
* Return : 1 if OK, 0 if error
|
|
||||||
*/
|
|
||||||
int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
|
|
||||||
|
|
||||||
/*
|
|
||||||
*_continue() :
|
|
||||||
These decoding functions allow decompression of multiple blocks in "streaming" mode.
|
|
||||||
Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB)
|
|
||||||
In the case of a ring buffers, decoding buffer must be either :
|
|
||||||
- Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
|
|
||||||
In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
|
|
||||||
- Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
|
|
||||||
maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block.
|
|
||||||
In which case, encoding and decoding buffers do not need to be synchronized,
|
|
||||||
and encoding ring buffer can have any size, including small ones ( < 64 KB).
|
|
||||||
- _At least_ 64 KB + 8 bytes + maxBlockSize.
|
|
||||||
In which case, encoding and decoding buffers do not need to be synchronized,
|
|
||||||
and encoding ring buffer can have any size, including larger than decoding buffer.
|
|
||||||
Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
|
|
||||||
and indicate where it is saved using LZ4_setStreamDecode()
|
|
||||||
*/
|
|
||||||
int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize);
|
|
||||||
int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Advanced decoding functions :
|
|
||||||
*_usingDict() :
|
|
||||||
These decoding functions work the same as
|
|
||||||
a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue()
|
|
||||||
They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure.
|
|
||||||
*/
|
|
||||||
int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize);
|
|
||||||
int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************
|
|
||||||
* Obsolete Functions
|
|
||||||
**************************************/
|
|
||||||
/* Deprecate Warnings */
|
|
||||||
/* Should these warnings messages be a problem,
|
|
||||||
it is generally possible to disable them,
|
|
||||||
with -Wno-deprecated-declarations for gcc
|
|
||||||
or _CRT_SECURE_NO_WARNINGS in Visual for example.
|
|
||||||
You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */
|
|
||||||
#ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK
|
|
||||||
# define LZ4_DEPRECATE_WARNING_DEFBLOCK
|
|
||||||
# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
|
||||||
# if (LZ4_GCC_VERSION >= 405) || defined(__clang__)
|
|
||||||
# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
|
|
||||||
# elif (LZ4_GCC_VERSION >= 301)
|
|
||||||
# define LZ4_DEPRECATED(message) __attribute__((deprecated))
|
|
||||||
# elif defined(_MSC_VER)
|
|
||||||
# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
|
|
||||||
# else
|
|
||||||
# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
|
|
||||||
# define LZ4_DEPRECATED(message)
|
|
||||||
# endif
|
|
||||||
#endif /* LZ4_DEPRECATE_WARNING_DEFBLOCK */
|
|
||||||
|
|
||||||
/* Obsolete compression functions */
|
|
||||||
/* These functions are planned to start generate warnings by r131 approximately */
|
|
||||||
int LZ4_compress (const char* source, char* dest, int sourceSize);
|
|
||||||
int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize);
|
|
||||||
int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
|
|
||||||
int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
|
|
||||||
int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
|
|
||||||
int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
|
|
||||||
|
|
||||||
/* Obsolete decompression functions */
|
|
||||||
/* These function names are completely deprecated and must no longer be used.
|
|
||||||
They are only provided here for compatibility with older programs.
|
|
||||||
- LZ4_uncompress is the same as LZ4_decompress_fast
|
|
||||||
- LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe
|
|
||||||
These function prototypes are now disabled; uncomment them only if you really need them.
|
|
||||||
It is highly recommended to stop using these prototypes and migrate to maintained ones */
|
|
||||||
/* int LZ4_uncompress (const char* source, char* dest, int outputSize); */
|
|
||||||
/* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */
|
|
||||||
|
|
||||||
/* Obsolete streaming functions; use new streaming interface whenever possible */
|
|
||||||
LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer);
|
|
||||||
LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void);
|
|
||||||
LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer);
|
|
||||||
LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state);
|
|
||||||
|
|
||||||
/* Obsolete streaming decoding functions */
|
|
||||||
LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
|
|
||||||
LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
|
|
||||||
|
|
||||||
|
|
||||||
#if defined (__cplusplus)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
/* $Id: connecthostport.c,v 1.15 2015/10/09 16:26:19 nanard Exp $ */
|
/* $Id: connecthostport.c,v 1.16 2016/12/16 08:57:53 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
* Copyright (c) 2010-2015 Thomas Bernard
|
* Copyright (c) 2010-2016 Thomas Bernard
|
||||||
* This software is subject to the conditions detailed in the
|
* This software is subject to the conditions detailed in the
|
||||||
* LICENCE file provided in this distribution. */
|
* LICENCE file provided in this distribution. */
|
||||||
|
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
|
|
||||||
/* use getaddrinfo() or gethostbyname()
|
/* use getaddrinfo() or gethostbyname()
|
||||||
* uncomment the following line in order to use gethostbyname() */
|
* uncomment the following line in order to use gethostbyname() */
|
||||||
#ifdef NO_GETADDRINFO
|
#ifdef NO_GETADDRINFO
|
||||||
@@ -102,13 +100,13 @@ int connecthostport(const char * host, unsigned short port,
|
|||||||
timeout.tv_usec = 0;
|
timeout.tv_usec = 0;
|
||||||
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||||
{
|
{
|
||||||
PRINT_SOCKET_ERROR("setsockopt");
|
PRINT_SOCKET_ERROR("setsockopt SO_RCVTIMEO");
|
||||||
}
|
}
|
||||||
timeout.tv_sec = 3;
|
timeout.tv_sec = 3;
|
||||||
timeout.tv_usec = 0;
|
timeout.tv_usec = 0;
|
||||||
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||||
{
|
{
|
||||||
PRINT_SOCKET_ERROR("setsockopt");
|
PRINT_SOCKET_ERROR("setsockopt SO_SNDTIMEO");
|
||||||
}
|
}
|
||||||
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
||||||
dest.sin_family = AF_INET;
|
dest.sin_family = AF_INET;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* $Id: minihttptestserver.c,v 1.19 2015/11/17 09:07:17 nanard Exp $ */
|
/* $Id: minihttptestserver.c,v 1.20 2016/12/16 08:54:55 nanard Exp $ */
|
||||||
/* Project : miniUPnP
|
/* Project : miniUPnP
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
* Copyright (c) 2011-2015 Thomas Bernard
|
* Copyright (c) 2011-2016 Thomas Bernard
|
||||||
* This software is subject to the conditions detailed in the
|
* This software is subject to the conditions detailed in the
|
||||||
* LICENCE file provided in this distribution.
|
* LICENCE file provided in this distribution.
|
||||||
* */
|
* */
|
||||||
@@ -611,7 +611,7 @@ int main(int argc, char * * argv) {
|
|||||||
if(pid < 0) {
|
if(pid < 0) {
|
||||||
perror("wait");
|
perror("wait");
|
||||||
} else {
|
} else {
|
||||||
printf("child(%d) terminated with status %d\n", pid, status);
|
printf("child(%d) terminated with status %d\n", (int)pid, status);
|
||||||
}
|
}
|
||||||
--child_to_wait_for;
|
--child_to_wait_for;
|
||||||
}
|
}
|
||||||
@@ -648,7 +648,7 @@ int main(int argc, char * * argv) {
|
|||||||
if(pid < 0) {
|
if(pid < 0) {
|
||||||
perror("wait");
|
perror("wait");
|
||||||
} else {
|
} else {
|
||||||
printf("child(%d) terminated with status %d\n", pid, status);
|
printf("child(%d) terminated with status %d\n", (int)pid, status);
|
||||||
}
|
}
|
||||||
--child_to_wait_for;
|
--child_to_wait_for;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
/* $Id: minisoap.c,v 1.24 2015/10/26 17:05:07 nanard Exp $ */
|
/* $Id: minisoap.c,v 1.24 2015/10/26 17:05:07 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
@@ -20,6 +19,7 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#endif
|
#endif
|
||||||
#include "minisoap.h"
|
#include "minisoap.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define OS_STRING "Win32"
|
#define OS_STRING "Win32"
|
||||||
#define MINIUPNPC_VERSION_STRING "2.0"
|
#define MINIUPNPC_VERSION_STRING "2.0"
|
||||||
@@ -124,3 +124,5 @@ int soapPostSubmit(int fd,
|
|||||||
#endif
|
#endif
|
||||||
return httpWrite(fd, body, bodysize, headerbuf, headerssize);
|
return httpWrite(fd, body, bodysize, headerbuf, headerssize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
#define _CRT_SECURE_NO_WARNINGS
|
/* $Id: minissdpc.c,v 1.33 2016/12/16 08:57:20 nanard Exp $ */
|
||||||
|
|
||||||
/* $Id: minissdpc.c,v 1.31 2016/01/19 09:56:46 nanard Exp $ */
|
|
||||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||||
* Project : miniupnp
|
* Project : miniupnp
|
||||||
* Web : http://miniupnp.free.fr/
|
* Web : http://miniupnp.free.fr/
|
||||||
* Author : Thomas BERNARD
|
* Author : Thomas BERNARD
|
||||||
* copyright (c) 2005-2015 Thomas Bernard
|
* copyright (c) 2005-2016 Thomas Bernard
|
||||||
* This software is subjet to the conditions detailed in the
|
* This software is subjet to the conditions detailed in the
|
||||||
* provided LICENCE file. */
|
* provided LICENCE file. */
|
||||||
/*#include <syslog.h>*/
|
/*#include <syslog.h>*/
|
||||||
@@ -13,6 +11,9 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#if defined (__NetBSD__)
|
||||||
|
#include <net/if.h>
|
||||||
|
#endif
|
||||||
#if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)
|
#if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
@@ -72,6 +73,9 @@ struct sockaddr_un {
|
|||||||
|
|
||||||
#if !defined(HAS_IP_MREQN) && !defined(_WIN32)
|
#if !defined(HAS_IP_MREQN) && !defined(_WIN32)
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
#if defined(__sun)
|
||||||
|
#include <sys/sockio.h>
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
|
#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
|
||||||
@@ -168,7 +172,7 @@ connectToMiniSSDPD(const char * socketpath)
|
|||||||
{
|
{
|
||||||
int s;
|
int s;
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
|
||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
||||||
|
|
||||||
@@ -179,19 +183,20 @@ connectToMiniSSDPD(const char * socketpath)
|
|||||||
perror("socket(unix)");
|
perror("socket(unix)");
|
||||||
return MINISSDPC_SOCKET_ERROR;
|
return MINISSDPC_SOCKET_ERROR;
|
||||||
}
|
}
|
||||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
|
||||||
/* setting a 3 seconds timeout */
|
/* setting a 3 seconds timeout */
|
||||||
|
/* not supported for AF_UNIX sockets under Solaris */
|
||||||
timeout.tv_sec = 3;
|
timeout.tv_sec = 3;
|
||||||
timeout.tv_usec = 0;
|
timeout.tv_usec = 0;
|
||||||
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||||
{
|
{
|
||||||
perror("setsockopt");
|
perror("setsockopt SO_RCVTIMEO unix");
|
||||||
}
|
}
|
||||||
timeout.tv_sec = 3;
|
timeout.tv_sec = 3;
|
||||||
timeout.tv_usec = 0;
|
timeout.tv_usec = 0;
|
||||||
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||||
{
|
{
|
||||||
perror("setsockopt");
|
perror("setsockopt SO_SNDTIMEO unix");
|
||||||
}
|
}
|
||||||
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
||||||
if(!socketpath)
|
if(!socketpath)
|
||||||
@@ -627,7 +632,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
|
|||||||
unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
|
unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
|
||||||
if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)
|
if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)
|
||||||
{
|
{
|
||||||
PRINT_SOCKET_ERROR("setsockopt");
|
PRINT_SOCKET_ERROR("setsockopt IPV6_MULTICAST_IF");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@@ -642,7 +647,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
|
|||||||
((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
|
((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
|
||||||
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
|
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
|
||||||
{
|
{
|
||||||
PRINT_SOCKET_ERROR("setsockopt");
|
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
#ifdef HAS_IP_MREQN
|
#ifdef HAS_IP_MREQN
|
||||||
@@ -652,7 +657,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
|
|||||||
reqn.imr_ifindex = if_nametoindex(multicastif);
|
reqn.imr_ifindex = if_nametoindex(multicastif);
|
||||||
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
|
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
|
||||||
{
|
{
|
||||||
PRINT_SOCKET_ERROR("setsockopt");
|
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
|
||||||
}
|
}
|
||||||
#elif !defined(_WIN32)
|
#elif !defined(_WIN32)
|
||||||
struct ifreq ifr;
|
struct ifreq ifr;
|
||||||
@@ -666,7 +671,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
|
|||||||
mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
|
mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
|
||||||
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
|
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
|
||||||
{
|
{
|
||||||
PRINT_SOCKET_ERROR("setsockopt");
|
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
|
||||||
}
|
}
|
||||||
#else /* _WIN32 */
|
#else /* _WIN32 */
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
|
|
||||||
/* $Id: miniupnpc.c,v 1.149 2016/02/09 09:50:46 nanard Exp $ */
|
/* $Id: miniupnpc.c,v 1.149 2016/02/09 09:50:46 nanard Exp $ */
|
||||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||||
* Project : miniupnp
|
* Project : miniupnp
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
#define UPNPDISCOVER_MEMORY_ERROR (-102)
|
#define UPNPDISCOVER_MEMORY_ERROR (-102)
|
||||||
|
|
||||||
/* versions : */
|
/* versions : */
|
||||||
#define MINIUPNPC_VERSION "2.0"
|
#define MINIUPNPC_VERSION "2.0.20161216"
|
||||||
#define MINIUPNPC_API_VERSION 16
|
#define MINIUPNPC_API_VERSION 16
|
||||||
|
|
||||||
/* Source port:
|
/* Source port:
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#define _CRT_SECURE_NO_WARNINGS
|
/* $Id: miniwget.c,v 1.76 2016/12/16 08:54:04 nanard Exp $ */
|
||||||
|
|
||||||
/* $Id: miniwget.c,v 1.75 2016/01/24 17:24:36 nanard Exp $ */
|
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* Website : http://miniupnp.free.fr/
|
* Website : http://miniupnp.free.fr/
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
@@ -50,6 +48,7 @@
|
|||||||
#define MIN(x,y) (((x)<(y))?(x):(y))
|
#define MIN(x,y) (((x)<(y))?(x):(y))
|
||||||
#endif /* MIN */
|
#endif /* MIN */
|
||||||
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define OS_STRING "Win32"
|
#define OS_STRING "Win32"
|
||||||
#define MINIUPNPC_VERSION_STRING "2.0"
|
#define MINIUPNPC_VERSION_STRING "2.0"
|
||||||
@@ -89,8 +88,10 @@ getHTTPResponse(int s, int * size, int * status_code)
|
|||||||
unsigned int content_buf_used = 0;
|
unsigned int content_buf_used = 0;
|
||||||
char chunksize_buf[32];
|
char chunksize_buf[32];
|
||||||
unsigned int chunksize_buf_index;
|
unsigned int chunksize_buf_index;
|
||||||
|
#ifdef DEBUG
|
||||||
char * reason_phrase = NULL;
|
char * reason_phrase = NULL;
|
||||||
int reason_phrase_len = 0;
|
int reason_phrase_len = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
if(status_code) *status_code = -1;
|
if(status_code) *status_code = -1;
|
||||||
header_buf = malloc(header_buf_len);
|
header_buf = malloc(header_buf_len);
|
||||||
@@ -187,8 +188,10 @@ getHTTPResponse(int s, int * size, int * status_code)
|
|||||||
*status_code = atoi(header_buf + sp + 1);
|
*status_code = atoi(header_buf + sp + 1);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
reason_phrase = header_buf + sp + 1;
|
reason_phrase = header_buf + sp + 1;
|
||||||
reason_phrase_len = i - sp - 1;
|
reason_phrase_len = i - sp - 1;
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
/* $Id: minixml.c,v 1.11 2014/02/03 15:54:12 nanard Exp $ */
|
/* $Id: minixml.c,v 1.11 2014/02/03 15:54:12 nanard Exp $ */
|
||||||
/* minixml.c : the minimum size a xml parser can be ! */
|
/* minixml.c : the minimum size a xml parser can be ! */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
/* $Id: minixmlvalid.c,v 1.7 2015/07/15 12:41:15 nanard Exp $ */
|
/* $Id: minixmlvalid.c,v 1.7 2015/07/15 12:41:15 nanard Exp $ */
|
||||||
/* MiniUPnP Project
|
/* MiniUPnP Project
|
||||||
* http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
|
* http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* $Id: portlistingparse.c,v 1.9 2015/07/15 12:41:13 nanard Exp $ */
|
/* $Id: portlistingparse.c,v 1.10 2016/12/16 08:53:21 nanard Exp $ */
|
||||||
/* MiniUPnP project
|
/* MiniUPnP project
|
||||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||||
* (c) 2011-2015 Thomas Bernard
|
* (c) 2011-2016 Thomas Bernard
|
||||||
* This software is subject to the conditions detailed
|
* This software is subject to the conditions detailed
|
||||||
* in the LICENCE file provided within the distribution */
|
* in the LICENCE file provided within the distribution */
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -55,7 +55,7 @@ startelt(void * d, const char * name, int l)
|
|||||||
pdata->curelt = PortMappingEltNone;
|
pdata->curelt = PortMappingEltNone;
|
||||||
for(i = 0; elements[i].str; i++)
|
for(i = 0; elements[i].str; i++)
|
||||||
{
|
{
|
||||||
if(memcmp(name, elements[i].str, l) == 0)
|
if(strlen(elements[i].str) == (size_t)l && memcmp(name, elements[i].str, l) == 0)
|
||||||
{
|
{
|
||||||
pdata->curelt = elements[i].code;
|
pdata->curelt = elements[i].code;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $Id: upnpc.c,v 1.114 2016/01/22 15:04:23 nanard Exp $ */
|
/* $Id: upnpc.c,v 1.115 2016/10/07 09:04:01 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
* Copyright (c) 2005-2016 Thomas Bernard
|
* Copyright (c) 2005-2016 Thomas Bernard
|
||||||
@@ -242,7 +242,7 @@ static void NewListRedirections(struct UPNPUrls * urls,
|
|||||||
* 2 - get extenal ip address
|
* 2 - get extenal ip address
|
||||||
* 3 - Add port mapping
|
* 3 - Add port mapping
|
||||||
* 4 - get this port mapping from the IGD */
|
* 4 - get this port mapping from the IGD */
|
||||||
static void SetRedirectAndTest(struct UPNPUrls * urls,
|
static int SetRedirectAndTest(struct UPNPUrls * urls,
|
||||||
struct IGDdatas * data,
|
struct IGDdatas * data,
|
||||||
const char * iaddr,
|
const char * iaddr,
|
||||||
const char * iport,
|
const char * iport,
|
||||||
@@ -262,13 +262,13 @@ static void SetRedirectAndTest(struct UPNPUrls * urls,
|
|||||||
if(!iaddr || !iport || !eport || !proto)
|
if(!iaddr || !iport || !eport || !proto)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Wrong arguments\n");
|
fprintf(stderr, "Wrong arguments\n");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
proto = protofix(proto);
|
proto = protofix(proto);
|
||||||
if(!proto)
|
if(!proto)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "invalid protocol\n");
|
fprintf(stderr, "invalid protocol\n");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = UPNP_GetExternalIPAddress(urls->controlURL,
|
r = UPNP_GetExternalIPAddress(urls->controlURL,
|
||||||
@@ -302,17 +302,19 @@ static void SetRedirectAndTest(struct UPNPUrls * urls,
|
|||||||
eport, proto, NULL/*remoteHost*/,
|
eport, proto, NULL/*remoteHost*/,
|
||||||
intClient, intPort, NULL/*desc*/,
|
intClient, intPort, NULL/*desc*/,
|
||||||
NULL/*enabled*/, duration);
|
NULL/*enabled*/, duration);
|
||||||
if(r!=UPNPCOMMAND_SUCCESS)
|
if(r!=UPNPCOMMAND_SUCCESS) {
|
||||||
printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n",
|
printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n",
|
||||||
r, strupnperror(r));
|
r, strupnperror(r));
|
||||||
else {
|
return -2;
|
||||||
|
} else {
|
||||||
printf("InternalIP:Port = %s:%s\n", intClient, intPort);
|
printf("InternalIP:Port = %s:%s\n", intClient, intPort);
|
||||||
printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n",
|
printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n",
|
||||||
externalIPAddress, eport, proto, intClient, intPort, duration);
|
externalIPAddress, eport, proto, intClient, intPort, duration);
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
RemoveRedirect(struct UPNPUrls * urls,
|
RemoveRedirect(struct UPNPUrls * urls,
|
||||||
struct IGDdatas * data,
|
struct IGDdatas * data,
|
||||||
const char * eport,
|
const char * eport,
|
||||||
@@ -323,19 +325,25 @@ RemoveRedirect(struct UPNPUrls * urls,
|
|||||||
if(!proto || !eport)
|
if(!proto || !eport)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "invalid arguments\n");
|
fprintf(stderr, "invalid arguments\n");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
proto = protofix(proto);
|
proto = protofix(proto);
|
||||||
if(!proto)
|
if(!proto)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "protocol invalid\n");
|
fprintf(stderr, "protocol invalid\n");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, remoteHost);
|
r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, remoteHost);
|
||||||
|
if(r!=UPNPCOMMAND_SUCCESS) {
|
||||||
|
printf("UPNP_DeletePortMapping() failed with code : %d\n", r);
|
||||||
|
return -2;
|
||||||
|
}else {
|
||||||
printf("UPNP_DeletePortMapping() returned : %d\n", r);
|
printf("UPNP_DeletePortMapping() returned : %d\n", r);
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
RemoveRedirectRange(struct UPNPUrls * urls,
|
RemoveRedirectRange(struct UPNPUrls * urls,
|
||||||
struct IGDdatas * data,
|
struct IGDdatas * data,
|
||||||
const char * ePortStart, char const * ePortEnd,
|
const char * ePortStart, char const * ePortEnd,
|
||||||
@@ -349,17 +357,23 @@ RemoveRedirectRange(struct UPNPUrls * urls,
|
|||||||
if(!proto || !ePortStart || !ePortEnd)
|
if(!proto || !ePortStart || !ePortEnd)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "invalid arguments\n");
|
fprintf(stderr, "invalid arguments\n");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
proto = protofix(proto);
|
proto = protofix(proto);
|
||||||
if(!proto)
|
if(!proto)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "protocol invalid\n");
|
fprintf(stderr, "protocol invalid\n");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
r = UPNP_DeletePortMappingRange(urls->controlURL, data->first.servicetype, ePortStart, ePortEnd, proto, manage);
|
r = UPNP_DeletePortMappingRange(urls->controlURL, data->first.servicetype, ePortStart, ePortEnd, proto, manage);
|
||||||
|
if(r!=UPNPCOMMAND_SUCCESS) {
|
||||||
|
printf("UPNP_DeletePortMappingRange() failed with code : %d\n", r);
|
||||||
|
return -2;
|
||||||
|
}else {
|
||||||
printf("UPNP_DeletePortMappingRange() returned : %d\n", r);
|
printf("UPNP_DeletePortMappingRange() returned : %d\n", r);
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* IGD:2, functions for service WANIPv6FirewallControl:1 */
|
/* IGD:2, functions for service WANIPv6FirewallControl:1 */
|
||||||
static void GetFirewallStatus(struct UPNPUrls * urls, struct IGDdatas * data)
|
static void GetFirewallStatus(struct UPNPUrls * urls, struct IGDdatas * data)
|
||||||
@@ -711,29 +725,33 @@ int main(int argc, char ** argv)
|
|||||||
NewListRedirections(&urls, &data);
|
NewListRedirections(&urls, &data);
|
||||||
break;
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
SetRedirectAndTest(&urls, &data,
|
if (SetRedirectAndTest(&urls, &data,
|
||||||
commandargv[0], commandargv[1],
|
commandargv[0], commandargv[1],
|
||||||
commandargv[2], commandargv[3],
|
commandargv[2], commandargv[3],
|
||||||
(commandargc > 4)?commandargv[4]:"0",
|
(commandargc > 4)?commandargv[4]:"0",
|
||||||
description, 0);
|
description, 0) < 0)
|
||||||
|
retcode = 2;
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
RemoveRedirect(&urls, &data, commandargv[0], commandargv[1],
|
if (RemoveRedirect(&urls, &data, commandargv[0], commandargv[1],
|
||||||
commandargc > 2 ? commandargv[2] : NULL);
|
commandargc > 2 ? commandargv[2] : NULL) < 0)
|
||||||
|
retcode = 2;
|
||||||
break;
|
break;
|
||||||
case 'n': /* aNy */
|
case 'n': /* aNy */
|
||||||
SetRedirectAndTest(&urls, &data,
|
if (SetRedirectAndTest(&urls, &data,
|
||||||
commandargv[0], commandargv[1],
|
commandargv[0], commandargv[1],
|
||||||
commandargv[2], commandargv[3],
|
commandargv[2], commandargv[3],
|
||||||
(commandargc > 4)?commandargv[4]:"0",
|
(commandargc > 4)?commandargv[4]:"0",
|
||||||
description, 1);
|
description, 1) < 0)
|
||||||
|
retcode = 2;
|
||||||
break;
|
break;
|
||||||
case 'N':
|
case 'N':
|
||||||
if (commandargc < 3)
|
if (commandargc < 3)
|
||||||
fprintf(stderr, "too few arguments\n");
|
fprintf(stderr, "too few arguments\n");
|
||||||
|
|
||||||
RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2],
|
if (RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2],
|
||||||
commandargc > 3 ? commandargv[3] : NULL);
|
commandargc > 3 ? commandargv[3] : NULL) < 0)
|
||||||
|
retcode = 2;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
GetConnectionStatus(&urls, &data);
|
GetConnectionStatus(&urls, &data);
|
||||||
@@ -749,17 +767,19 @@ int main(int argc, char ** argv)
|
|||||||
break;
|
break;
|
||||||
} else if(is_int(commandargv[i+1])){
|
} else if(is_int(commandargv[i+1])){
|
||||||
/* 2nd parameter is an integer : <port> <external_port> <protocol> */
|
/* 2nd parameter is an integer : <port> <external_port> <protocol> */
|
||||||
SetRedirectAndTest(&urls, &data,
|
if (SetRedirectAndTest(&urls, &data,
|
||||||
lanaddr, commandargv[i],
|
lanaddr, commandargv[i],
|
||||||
commandargv[i+1], commandargv[i+2], "0",
|
commandargv[i+1], commandargv[i+2], "0",
|
||||||
description, 0);
|
description, 0) < 0)
|
||||||
|
retcode = 2;
|
||||||
i+=3; /* 3 parameters parsed */
|
i+=3; /* 3 parameters parsed */
|
||||||
} else {
|
} else {
|
||||||
/* 2nd parameter not an integer : <port> <protocol> */
|
/* 2nd parameter not an integer : <port> <protocol> */
|
||||||
SetRedirectAndTest(&urls, &data,
|
if (SetRedirectAndTest(&urls, &data,
|
||||||
lanaddr, commandargv[i],
|
lanaddr, commandargv[i],
|
||||||
commandargv[i], commandargv[i+1], "0",
|
commandargv[i], commandargv[i+1], "0",
|
||||||
description, 0);
|
description, 0) < 0)
|
||||||
|
retcode = 2;
|
||||||
i+=2; /* 2 parameters parsed */
|
i+=2; /* 2 parameters parsed */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
|
|
||||||
/* $Id: upnpcommands.c,v 1.47 2016/03/07 12:26:48 nanard Exp $ */
|
/* $Id: upnpcommands.c,v 1.47 2016/03/07 12:26:48 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
/* $Id: upnpreplyparse.c,v 1.19 2015/07/15 10:29:11 nanard Exp $ */
|
/* $Id: upnpreplyparse.c,v 1.19 2015/07/15 10:29:11 nanard Exp $ */
|
||||||
/* MiniUPnP project
|
/* MiniUPnP project
|
||||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||||
|
|||||||
@@ -96,21 +96,31 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
#define ZT_MAX_NETWORK_SPECIALISTS 256
|
#define ZT_MAX_NETWORK_SPECIALISTS 256
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of static physical to ZeroTier address mappings (typically relays, etc.)
|
|
||||||
*/
|
|
||||||
#define ZT_MAX_NETWORK_PINNED 16
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of rule table entries per network (can be increased)
|
|
||||||
*/
|
|
||||||
#define ZT_MAX_NETWORK_RULES 256
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of multicast group subscriptions per network
|
* Maximum number of multicast group subscriptions per network
|
||||||
*/
|
*/
|
||||||
#define ZT_MAX_NETWORK_MULTICAST_SUBSCRIPTIONS 4096
|
#define ZT_MAX_NETWORK_MULTICAST_SUBSCRIPTIONS 4096
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rules engine revision ID, which specifies rules engine capabilities
|
||||||
|
*/
|
||||||
|
#define ZT_RULES_ENGINE_REVISION 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of base (non-capability) network rules
|
||||||
|
*/
|
||||||
|
#define ZT_MAX_NETWORK_RULES 1024
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of per-member capabilities per network
|
||||||
|
*/
|
||||||
|
#define ZT_MAX_NETWORK_CAPABILITIES 128
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of per-member tags per network
|
||||||
|
*/
|
||||||
|
#define ZT_MAX_NETWORK_TAGS 128
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of direct network paths to a given peer
|
* Maximum number of direct network paths to a given peer
|
||||||
*/
|
*/
|
||||||
@@ -121,6 +131,21 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
#define ZT_MAX_TRUSTED_PATHS 16
|
#define ZT_MAX_TRUSTED_PATHS 16
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of rules per capability
|
||||||
|
*/
|
||||||
|
#define ZT_MAX_CAPABILITY_RULES 64
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of certificates of ownership to assign to a single network member
|
||||||
|
*/
|
||||||
|
#define ZT_MAX_CERTIFICATES_OF_OWNERSHIP 4
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global maximum length for capability chain of custody (including initial issue)
|
||||||
|
*/
|
||||||
|
#define ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH 7
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of hops in a ZeroTier circuit test
|
* Maximum number of hops in a ZeroTier circuit test
|
||||||
*
|
*
|
||||||
@@ -134,6 +159,11 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
#define ZT_CIRCUIT_TEST_MAX_HOP_BREADTH 8
|
#define ZT_CIRCUIT_TEST_MAX_HOP_BREADTH 8
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Circuit test report flag: upstream peer authorized in path (e.g. by network COM)
|
||||||
|
*/
|
||||||
|
#define ZT_CIRCUIT_TEST_REPORT_FLAGS_UPSTREAM_AUTHORIZED_IN_PATH 0x0000000000000001ULL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of cluster members (and max member ID plus one)
|
* Maximum number of cluster members (and max member ID plus one)
|
||||||
*/
|
*/
|
||||||
@@ -149,6 +179,96 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
#define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1500 - 48)
|
#define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1500 - 48)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum value for link quality (min is 0)
|
||||||
|
*/
|
||||||
|
#define ZT_PATH_LINK_QUALITY_MAX 0xff
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: packet direction, 1 if inbound 0 if outbound
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_INBOUND 0x8000000000000000ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: multicast or broadcast destination MAC
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_MULTICAST 0x4000000000000000ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: broadcast destination MAC
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_BROADCAST 0x2000000000000000ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: sending IP address has a certificate of ownership
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_SENDER_IP_AUTHENTICATED 0x1000000000000000ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: sending MAC address has a certificate of ownership
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_SENDER_MAC_AUTHENTICATED 0x0800000000000000ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: TCP left-most reserved bit
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_RESERVED_0 0x0000000000000800ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: TCP middle reserved bit
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_RESERVED_1 0x0000000000000400ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: TCP right-most reserved bit
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_RESERVED_2 0x0000000000000200ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: TCP NS flag
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_NS 0x0000000000000100ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: TCP CWR flag
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_CWR 0x0000000000000080ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: TCP ECE flag
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_ECE 0x0000000000000040ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: TCP URG flag
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_URG 0x0000000000000020ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: TCP ACK flag
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_ACK 0x0000000000000010ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: TCP PSH flag
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_PSH 0x0000000000000008ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: TCP RST flag
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_RST 0x0000000000000004ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: TCP SYN flag
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN 0x0000000000000002ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet characteristics flag: TCP FIN flag
|
||||||
|
*/
|
||||||
|
#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN 0x0000000000000001ULL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A null/empty sockaddr (all zero) to signify an unspecified socket address
|
* A null/empty sockaddr (all zero) to signify an unspecified socket address
|
||||||
*/
|
*/
|
||||||
@@ -293,9 +413,45 @@ enum ZT_Event
|
|||||||
*
|
*
|
||||||
* Meta-data: C string, TRACE message
|
* Meta-data: C string, TRACE message
|
||||||
*/
|
*/
|
||||||
ZT_EVENT_TRACE = 5
|
ZT_EVENT_TRACE = 5,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VERB_USER_MESSAGE received
|
||||||
|
*
|
||||||
|
* These are generated when a VERB_USER_MESSAGE packet is received via
|
||||||
|
* ZeroTier VL1.
|
||||||
|
*
|
||||||
|
* Meta-data: ZT_UserMessage structure
|
||||||
|
*/
|
||||||
|
ZT_EVENT_USER_MESSAGE = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User message used with ZT_EVENT_USER_MESSAGE
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* ZeroTier address of sender (least significant 40 bits)
|
||||||
|
*/
|
||||||
|
uint64_t origin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User message type ID
|
||||||
|
*/
|
||||||
|
uint64_t typeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User message data (not including type ID)
|
||||||
|
*/
|
||||||
|
const void *data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of data in bytes
|
||||||
|
*/
|
||||||
|
unsigned int length;
|
||||||
|
} ZT_UserMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current node status
|
* Current node status
|
||||||
*/
|
*/
|
||||||
@@ -306,16 +462,6 @@ typedef struct
|
|||||||
*/
|
*/
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
|
|
||||||
/**
|
|
||||||
* Current world ID
|
|
||||||
*/
|
|
||||||
uint64_t worldId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current world revision/timestamp
|
|
||||||
*/
|
|
||||||
uint64_t worldTimestamp;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public identity in string-serialized form (safe to send to others)
|
* Public identity in string-serialized form (safe to send to others)
|
||||||
*
|
*
|
||||||
@@ -391,12 +537,16 @@ enum ZT_VirtualNetworkType
|
|||||||
/**
|
/**
|
||||||
* The type of a virtual network rules table entry
|
* The type of a virtual network rules table entry
|
||||||
*
|
*
|
||||||
* These must range from 0 to 127 (0x7f).
|
* These must be from 0 to 63 since the most significant two bits of each
|
||||||
|
* rule type are NOT (MSB) and AND/OR.
|
||||||
*
|
*
|
||||||
* Each rule is composed of one or more MATCHes followed by an ACTION.
|
* Each rule is composed of zero or more MATCHes followed by an ACTION.
|
||||||
|
* An ACTION with no MATCHes is always taken.
|
||||||
*/
|
*/
|
||||||
enum ZT_VirtualNetworkRuleType
|
enum ZT_VirtualNetworkRuleType
|
||||||
{
|
{
|
||||||
|
// 0 to 15 reserved for actions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drop frame
|
* Drop frame
|
||||||
*/
|
*/
|
||||||
@@ -408,129 +558,69 @@ enum ZT_VirtualNetworkRuleType
|
|||||||
ZT_NETWORK_RULE_ACTION_ACCEPT = 1,
|
ZT_NETWORK_RULE_ACTION_ACCEPT = 1,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forward a copy of this frame to an observer
|
* Forward a copy of this frame to an observer (by ZT address)
|
||||||
*/
|
*/
|
||||||
ZT_NETWORK_RULE_ACTION_TEE = 2,
|
ZT_NETWORK_RULE_ACTION_TEE = 2,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Explicitly redirect this frame to another device (ignored if this is the target device)
|
* Exactly like TEE but mandates ACKs from observer
|
||||||
*/
|
*/
|
||||||
ZT_NETWORK_RULE_ACTION_REDIRECT = 3,
|
ZT_NETWORK_RULE_ACTION_WATCH = 3,
|
||||||
|
|
||||||
// <32 == actions
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Source ZeroTier address -- analogous to an Ethernet port ID on a switch
|
* Drop and redirect this frame to another node (by ZT address)
|
||||||
*/
|
*/
|
||||||
ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS = 32,
|
ZT_NETWORK_RULE_ACTION_REDIRECT = 4,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destination ZeroTier address -- analogous to an Ethernet port ID on a switch
|
* Stop evaluating rule set (drops unless there are capabilities, etc.)
|
||||||
*/
|
*/
|
||||||
ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS = 33,
|
ZT_NETWORK_RULE_ACTION_BREAK = 5,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ethernet VLAN ID
|
* Maximum ID for an ACTION, anything higher is a MATCH
|
||||||
*/
|
*/
|
||||||
ZT_NETWORK_RULE_MATCH_VLAN_ID = 34,
|
ZT_NETWORK_RULE_ACTION__MAX_ID = 15,
|
||||||
|
|
||||||
/**
|
// 16 to 63 reserved for match criteria
|
||||||
* Ethernet VLAN PCP
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_VLAN_PCP = 35,
|
|
||||||
|
|
||||||
/**
|
ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS = 24,
|
||||||
* Ethernet VLAN DEI
|
ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS = 25,
|
||||||
*/
|
ZT_NETWORK_RULE_MATCH_VLAN_ID = 26,
|
||||||
ZT_NETWORK_RULE_MATCH_VLAN_DEI = 36,
|
ZT_NETWORK_RULE_MATCH_VLAN_PCP = 27,
|
||||||
|
ZT_NETWORK_RULE_MATCH_VLAN_DEI = 28,
|
||||||
/**
|
ZT_NETWORK_RULE_MATCH_MAC_SOURCE = 29,
|
||||||
* Ethernet frame type
|
ZT_NETWORK_RULE_MATCH_MAC_DEST = 30,
|
||||||
*/
|
ZT_NETWORK_RULE_MATCH_IPV4_SOURCE = 31,
|
||||||
|
ZT_NETWORK_RULE_MATCH_IPV4_DEST = 32,
|
||||||
|
ZT_NETWORK_RULE_MATCH_IPV6_SOURCE = 33,
|
||||||
|
ZT_NETWORK_RULE_MATCH_IPV6_DEST = 34,
|
||||||
|
ZT_NETWORK_RULE_MATCH_IP_TOS = 35,
|
||||||
|
ZT_NETWORK_RULE_MATCH_IP_PROTOCOL = 36,
|
||||||
ZT_NETWORK_RULE_MATCH_ETHERTYPE = 37,
|
ZT_NETWORK_RULE_MATCH_ETHERTYPE = 37,
|
||||||
|
ZT_NETWORK_RULE_MATCH_ICMP = 38,
|
||||||
|
ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE = 39,
|
||||||
|
ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE = 40,
|
||||||
|
ZT_NETWORK_RULE_MATCH_CHARACTERISTICS = 41,
|
||||||
|
ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE = 42,
|
||||||
|
ZT_NETWORK_RULE_MATCH_RANDOM = 43,
|
||||||
|
ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE = 44,
|
||||||
|
ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND = 45,
|
||||||
|
ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR = 46,
|
||||||
|
ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR = 47,
|
||||||
|
ZT_NETWORK_RULE_MATCH_TAGS_EQUAL = 48,
|
||||||
|
ZT_NETWORK_RULE_MATCH_TAG_SENDER = 49,
|
||||||
|
ZT_NETWORK_RULE_MATCH_TAG_RECEIVER = 50,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Source Ethernet MAC address
|
* Maximum ID allowed for a MATCH entry in the rules table
|
||||||
*/
|
*/
|
||||||
ZT_NETWORK_RULE_MATCH_MAC_SOURCE = 38,
|
ZT_NETWORK_RULE_MATCH__MAX_ID = 63
|
||||||
|
|
||||||
/**
|
|
||||||
* Destination Ethernet MAC address
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_MAC_DEST = 39,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source IPv4 address
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_IPV4_SOURCE = 40,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destination IPv4 address
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_IPV4_DEST = 41,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source IPv6 address
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_IPV6_SOURCE = 42,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destination IPv6 address
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_IPV6_DEST = 43,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IP TOS (type of service)
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_IP_TOS = 44,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IP protocol
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_IP_PROTOCOL = 45,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IP source port range (start-end, inclusive)
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE = 46,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IP destination port range (start-end, inclusive)
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE = 47,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Packet characteristics (set of flags)
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_CHARACTERISTICS = 48,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Frame size range (start-end, inclusive)
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE = 49,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Match a range of relative TCP sequence numbers (e.g. approx first N bytes of stream)
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE = 50,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Match a certificate of network membership field from the ZT origin's COM: greater than or equal to
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_COM_FIELD_GE = 51,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Match a certificate of network membership field from the ZT origin's COM: less than or equal to
|
|
||||||
*/
|
|
||||||
ZT_NETWORK_RULE_MATCH_COM_FIELD_LE = 52
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Network flow rule
|
* Network flow rule
|
||||||
*
|
*
|
||||||
* NOTE: Currently (1.1.x) only etherType is supported! Other things will
|
|
||||||
* have no effect until the rules engine is fully implemented.
|
|
||||||
*
|
|
||||||
* Rules are stored in a table in which one or more match entries is followed
|
* Rules are stored in a table in which one or more match entries is followed
|
||||||
* by an action. If more than one match precedes an action, the rule is
|
* by an action. If more than one match precedes an action, the rule is
|
||||||
* the AND of all matches. An action with no match is always taken since it
|
* the AND of all matches. An action with no match is always taken since it
|
||||||
@@ -542,15 +632,15 @@ enum ZT_VirtualNetworkRuleType
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Least significant 7 bits: ZT_VirtualNetworkRuleType, most significant 1 bit is NOT bit
|
* Type and flags
|
||||||
*
|
*
|
||||||
* If the NOT bit is set, then matches will be interpreted as "does not
|
* Bits are: NOTTTTTT
|
||||||
* match." The NOT bit has no effect on actions.
|
|
||||||
*
|
*
|
||||||
* Use "& 0x7f" to get the enum and "& 0x80" to get the NOT flag.
|
* N - If true, sense of match is inverted (no effect on actions)
|
||||||
|
* O - If true, result is ORed with previous instead of ANDed (no effect on actions)
|
||||||
|
* T - Rule or action type
|
||||||
*
|
*
|
||||||
* The union 'v' is a variant type, and this selects which field in 'v' is
|
* AND with 0x3f to get type, 0x80 to get NOT bit, and 0x40 to get OR bit.
|
||||||
* actually used and valid.
|
|
||||||
*/
|
*/
|
||||||
uint8_t t;
|
uint8_t t;
|
||||||
|
|
||||||
@@ -584,16 +674,16 @@ typedef struct
|
|||||||
*/
|
*/
|
||||||
uint16_t port[2];
|
uint16_t port[2];
|
||||||
|
|
||||||
/**
|
|
||||||
* TCP relative sequence number range -- start-end inclusive -- host byte order
|
|
||||||
*/
|
|
||||||
uint32_t tcpseq[2];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 40-bit ZeroTier address (in least significant bits, host byte order)
|
* 40-bit ZeroTier address (in least significant bits, host byte order)
|
||||||
*/
|
*/
|
||||||
uint64_t zt;
|
uint64_t zt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 0 = never, UINT32_MAX = always
|
||||||
|
*/
|
||||||
|
uint32_t randomProbability;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 48-bit Ethernet MAC address in big-endian order
|
* 48-bit Ethernet MAC address in big-endian order
|
||||||
*/
|
*/
|
||||||
@@ -625,9 +715,12 @@ typedef struct
|
|||||||
uint8_t ipProtocol;
|
uint8_t ipProtocol;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IP type of service
|
* IP type of service a.k.a. DSCP field
|
||||||
*/
|
*/
|
||||||
uint8_t ipTos;
|
struct {
|
||||||
|
uint8_t mask;
|
||||||
|
uint8_t value[2];
|
||||||
|
} ipTos;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ethernet packet size in host byte order (start-end, inclusive)
|
* Ethernet packet size in host byte order (start-end, inclusive)
|
||||||
@@ -635,12 +728,52 @@ typedef struct
|
|||||||
uint16_t frameSize[2];
|
uint16_t frameSize[2];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* COM ID and value for ZT_NETWORK_RULE_MATCH_COM_FIELD_GE and ZT_NETWORK_RULE_MATCH_COM_FIELD_LE
|
* ICMP type and code
|
||||||
*/
|
*/
|
||||||
uint64_t comIV[2];
|
struct {
|
||||||
|
uint8_t type; // ICMP type, always matched
|
||||||
|
uint8_t code; // ICMP code if matched
|
||||||
|
uint8_t flags; // flag 0x01 means also match code, otherwise only match type
|
||||||
|
} icmp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For tag-related rules
|
||||||
|
*/
|
||||||
|
struct {
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t value;
|
||||||
|
} tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destinations for TEE and REDIRECT
|
||||||
|
*/
|
||||||
|
struct {
|
||||||
|
uint64_t address;
|
||||||
|
uint32_t flags;
|
||||||
|
uint16_t length;
|
||||||
|
} fwd;
|
||||||
} v;
|
} v;
|
||||||
} ZT_VirtualNetworkRule;
|
} ZT_VirtualNetworkRule;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 128-bit ID (GUID) of this capability
|
||||||
|
*/
|
||||||
|
uint64_t id[2];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expiration time (measured vs. network config timestamp issued by controller)
|
||||||
|
*/
|
||||||
|
uint64_t expiration;
|
||||||
|
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint64_t from;
|
||||||
|
uint64_t to;
|
||||||
|
} custody[ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH];
|
||||||
|
} ZT_VirtualNetworkCapability;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A route to be pushed on a virtual network
|
* A route to be pushed on a virtual network
|
||||||
*/
|
*/
|
||||||
@@ -712,16 +845,18 @@ enum ZT_VirtualNetworkConfigOperation
|
|||||||
/**
|
/**
|
||||||
* What trust hierarchy role does this peer have?
|
* What trust hierarchy role does this peer have?
|
||||||
*/
|
*/
|
||||||
enum ZT_PeerRole {
|
enum ZT_PeerRole
|
||||||
|
{
|
||||||
ZT_PEER_ROLE_LEAF = 0, // ordinary node
|
ZT_PEER_ROLE_LEAF = 0, // ordinary node
|
||||||
ZT_PEER_ROLE_RELAY = 1, // relay node
|
ZT_PEER_ROLE_MOON = 1, // moon root
|
||||||
ZT_PEER_ROLE_ROOT = 2 // root server
|
ZT_PEER_ROLE_PLANET = 2 // planetary root
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vendor ID
|
* Vendor ID
|
||||||
*/
|
*/
|
||||||
enum ZT_Vendor {
|
enum ZT_Vendor
|
||||||
|
{
|
||||||
ZT_VENDOR_UNSPECIFIED = 0,
|
ZT_VENDOR_UNSPECIFIED = 0,
|
||||||
ZT_VENDOR_ZEROTIER = 1
|
ZT_VENDOR_ZEROTIER = 1
|
||||||
};
|
};
|
||||||
@@ -729,7 +864,8 @@ enum ZT_Vendor {
|
|||||||
/**
|
/**
|
||||||
* Platform type
|
* Platform type
|
||||||
*/
|
*/
|
||||||
enum ZT_Platform {
|
enum ZT_Platform
|
||||||
|
{
|
||||||
ZT_PLATFORM_UNSPECIFIED = 0,
|
ZT_PLATFORM_UNSPECIFIED = 0,
|
||||||
ZT_PLATFORM_LINUX = 1,
|
ZT_PLATFORM_LINUX = 1,
|
||||||
ZT_PLATFORM_WINDOWS = 2,
|
ZT_PLATFORM_WINDOWS = 2,
|
||||||
@@ -744,13 +880,15 @@ enum ZT_Platform {
|
|||||||
ZT_PLATFORM_VXWORKS = 11,
|
ZT_PLATFORM_VXWORKS = 11,
|
||||||
ZT_PLATFORM_FREERTOS = 12,
|
ZT_PLATFORM_FREERTOS = 12,
|
||||||
ZT_PLATFORM_SYSBIOS = 13,
|
ZT_PLATFORM_SYSBIOS = 13,
|
||||||
ZT_PLATFORM_HURD = 14
|
ZT_PLATFORM_HURD = 14,
|
||||||
|
ZT_PLATFORM_WEB = 15
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Architecture type
|
* Architecture type
|
||||||
*/
|
*/
|
||||||
enum ZT_Architecture {
|
enum ZT_Architecture
|
||||||
|
{
|
||||||
ZT_ARCHITECTURE_UNSPECIFIED = 0,
|
ZT_ARCHITECTURE_UNSPECIFIED = 0,
|
||||||
ZT_ARCHITECTURE_X86 = 1,
|
ZT_ARCHITECTURE_X86 = 1,
|
||||||
ZT_ARCHITECTURE_X64 = 2,
|
ZT_ARCHITECTURE_X64 = 2,
|
||||||
@@ -765,7 +903,8 @@ enum ZT_Architecture {
|
|||||||
ZT_ARCHITECTURE_SPARC32 = 11,
|
ZT_ARCHITECTURE_SPARC32 = 11,
|
||||||
ZT_ARCHITECTURE_SPARC64 = 12,
|
ZT_ARCHITECTURE_SPARC64 = 12,
|
||||||
ZT_ARCHITECTURE_DOTNET_CLR = 13,
|
ZT_ARCHITECTURE_DOTNET_CLR = 13,
|
||||||
ZT_ARCHITECTURE_JAVA_JVM = 14
|
ZT_ARCHITECTURE_JAVA_JVM = 14,
|
||||||
|
ZT_ARCHITECTURE_WEB = 15
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -803,6 +942,11 @@ typedef struct
|
|||||||
*/
|
*/
|
||||||
unsigned int mtu;
|
unsigned int mtu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recommended MTU to avoid fragmentation at the physical layer (hint)
|
||||||
|
*/
|
||||||
|
unsigned int physicalMtu;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If nonzero, the network this port belongs to indicates DHCP availability
|
* If nonzero, the network this port belongs to indicates DHCP availability
|
||||||
*
|
*
|
||||||
@@ -898,9 +1042,14 @@ typedef struct
|
|||||||
uint64_t trustedPathId;
|
uint64_t trustedPathId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is path active?
|
* Path link quality from 0 to 255 (always 255 if peer does not support)
|
||||||
*/
|
*/
|
||||||
int active;
|
int linkQuality;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is path expired?
|
||||||
|
*/
|
||||||
|
int expired;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is path preferred?
|
* Is path preferred?
|
||||||
@@ -918,16 +1067,6 @@ typedef struct
|
|||||||
*/
|
*/
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
|
|
||||||
/**
|
|
||||||
* Time we last received a unicast frame from this peer
|
|
||||||
*/
|
|
||||||
uint64_t lastUnicastFrame;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Time we last received a multicast rame from this peer
|
|
||||||
*/
|
|
||||||
uint64_t lastMulticastFrame;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remote major version or -1 if not known
|
* Remote major version or -1 if not known
|
||||||
*/
|
*/
|
||||||
@@ -1062,18 +1201,13 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
|
|
||||||
/**
|
|
||||||
* Timestamp on remote device
|
|
||||||
*/
|
|
||||||
uint64_t remoteTimestamp;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 64-bit packet ID of packet received by the reporting device
|
* 64-bit packet ID of packet received by the reporting device
|
||||||
*/
|
*/
|
||||||
uint64_t sourcePacketId;
|
uint64_t sourcePacketId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flags (currently unused, will be zero)
|
* Flags
|
||||||
*/
|
*/
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
|
|
||||||
@@ -1136,6 +1270,11 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
struct sockaddr_storage receivedFromRemoteAddress;
|
struct sockaddr_storage receivedFromRemoteAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path link quality of physical path over which test was received
|
||||||
|
*/
|
||||||
|
int receivedFromLinkQuality;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Next hops to which packets are being or will be sent by the reporter
|
* Next hops to which packets are being or will be sent by the reporter
|
||||||
*
|
*
|
||||||
@@ -1402,8 +1541,9 @@ typedef int (*ZT_WirePacketSendFunction)(
|
|||||||
* Paramters:
|
* Paramters:
|
||||||
* (1) Node
|
* (1) Node
|
||||||
* (2) User pointer
|
* (2) User pointer
|
||||||
* (3) Local interface address
|
* (3) ZeroTier address or 0 for none/any
|
||||||
* (4) Remote address
|
* (4) Local interface address
|
||||||
|
* (5) Remote address
|
||||||
*
|
*
|
||||||
* This function must return nonzero (true) if the path should be used.
|
* This function must return nonzero (true) if the path should be used.
|
||||||
*
|
*
|
||||||
@@ -1422,40 +1562,103 @@ typedef int (*ZT_WirePacketSendFunction)(
|
|||||||
typedef int (*ZT_PathCheckFunction)(
|
typedef int (*ZT_PathCheckFunction)(
|
||||||
ZT_Node *, /* Node */
|
ZT_Node *, /* Node */
|
||||||
void *, /* User ptr */
|
void *, /* User ptr */
|
||||||
|
uint64_t, /* ZeroTier address */
|
||||||
const struct sockaddr_storage *, /* Local address */
|
const struct sockaddr_storage *, /* Local address */
|
||||||
const struct sockaddr_storage *); /* Remote address */
|
const struct sockaddr_storage *); /* Remote address */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to get physical addresses for ZeroTier peers
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* (1) Node
|
||||||
|
* (2) User pointer
|
||||||
|
* (3) ZeroTier address (least significant 40 bits)
|
||||||
|
* (4) Desried address family or -1 for any
|
||||||
|
* (5) Buffer to fill with result
|
||||||
|
*
|
||||||
|
* If provided this function will be occasionally called to get physical
|
||||||
|
* addresses that might be tried to reach a ZeroTier address. It must
|
||||||
|
* return a nonzero (true) value if the result buffer has been filled
|
||||||
|
* with an address.
|
||||||
|
*/
|
||||||
|
typedef int (*ZT_PathLookupFunction)(
|
||||||
|
ZT_Node *, /* Node */
|
||||||
|
void *, /* User ptr */
|
||||||
|
uint64_t, /* ZeroTier address (40 bits) */
|
||||||
|
int, /* Desired ss_family or -1 for any */
|
||||||
|
struct sockaddr_storage *); /* Result buffer */
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
/* C Node API */
|
/* C Node API */
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure for configuring ZeroTier core callback functions
|
||||||
|
*/
|
||||||
|
struct ZT_Node_Callbacks
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Struct version -- must currently be 0
|
||||||
|
*/
|
||||||
|
long version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REQUIRED: Function to get objects from persistent storage
|
||||||
|
*/
|
||||||
|
ZT_DataStoreGetFunction dataStoreGetFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REQUIRED: Function to store objects in persistent storage
|
||||||
|
*/
|
||||||
|
ZT_DataStorePutFunction dataStorePutFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REQUIRED: Function to send packets over the physical wire
|
||||||
|
*/
|
||||||
|
ZT_WirePacketSendFunction wirePacketSendFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REQUIRED: Function to inject frames into a virtual network's TAP
|
||||||
|
*/
|
||||||
|
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REQUIRED: Function to be called when virtual networks are configured or changed
|
||||||
|
*/
|
||||||
|
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REQUIRED: Function to be called to notify external code of important events
|
||||||
|
*/
|
||||||
|
ZT_EventCallback eventCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OPTIONAL: Function to check whether a given physical path should be used
|
||||||
|
*/
|
||||||
|
ZT_PathCheckFunction pathCheckFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OPTIONAL: Function to get hints to physical paths to ZeroTier addresses
|
||||||
|
*/
|
||||||
|
ZT_PathLookupFunction pathLookupFunction;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new ZeroTier One node
|
* Create a new ZeroTier One node
|
||||||
*
|
*
|
||||||
* Note that this can take a few seconds the first time it's called, as it
|
* Note that this can take a few seconds the first time it's called, as it
|
||||||
* will generate an identity.
|
* will generate an identity.
|
||||||
*
|
*
|
||||||
|
* TODO: should consolidate function pointers into versioned structure for
|
||||||
|
* better API stability.
|
||||||
|
*
|
||||||
* @param node Result: pointer is set to new node instance on success
|
* @param node Result: pointer is set to new node instance on success
|
||||||
* @param uptr User pointer to pass to functions/callbacks
|
* @param uptr User pointer to pass to functions/callbacks
|
||||||
|
* @param callbacks Callback function configuration
|
||||||
* @param now Current clock in milliseconds
|
* @param now Current clock in milliseconds
|
||||||
* @param dataStoreGetFunction Function called to get objects from persistent storage
|
|
||||||
* @param dataStorePutFunction Function called to put objects in persistent storage
|
|
||||||
* @param virtualNetworkConfigFunction Function to be called when virtual LANs are created, deleted, or their config parameters change
|
|
||||||
* @param pathCheckFunction A function to check whether a path should be used for ZeroTier traffic, or NULL to allow any path
|
|
||||||
* @param eventCallback Function to receive status updates and non-fatal error notices
|
|
||||||
* @return OK (0) or error code if a fatal error condition has occurred
|
* @return OK (0) or error code if a fatal error condition has occurred
|
||||||
*/
|
*/
|
||||||
enum ZT_ResultCode ZT_Node_new(
|
enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now);
|
||||||
ZT_Node **node,
|
|
||||||
void *uptr,
|
|
||||||
uint64_t now,
|
|
||||||
ZT_DataStoreGetFunction dataStoreGetFunction,
|
|
||||||
ZT_DataStorePutFunction dataStorePutFunction,
|
|
||||||
ZT_WirePacketSendFunction wirePacketSendFunction,
|
|
||||||
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
|
|
||||||
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
|
|
||||||
ZT_PathCheckFunction pathCheckFunction,
|
|
||||||
ZT_EventCallback eventCallback);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a node and free all resources it consumes
|
* Delete a node and free all resources it consumes
|
||||||
@@ -1601,6 +1804,29 @@ enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,uint64_t nwid,uint64
|
|||||||
*/
|
*/
|
||||||
enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
|
enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add or update a moon
|
||||||
|
*
|
||||||
|
* Moons are persisted in the data store in moons.d/, so this can persist
|
||||||
|
* across invocations if the contents of moon.d are scanned and orbit is
|
||||||
|
* called for each on startup.
|
||||||
|
*
|
||||||
|
* @param moonWorldId Moon's world ID
|
||||||
|
* @param moonSeed If non-zero, the ZeroTier address of any member of the moon to query for moon definition
|
||||||
|
* @param len Length of moonWorld in bytes
|
||||||
|
* @return Error if moon was invalid or failed to be added
|
||||||
|
*/
|
||||||
|
enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,uint64_t moonWorldId,uint64_t moonSeed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a moon (does nothing if not present)
|
||||||
|
*
|
||||||
|
* @param node Node instance
|
||||||
|
* @param moonWorldId World ID of moon to remove
|
||||||
|
* @return Error if anything bad happened
|
||||||
|
*/
|
||||||
|
enum ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,uint64_t moonWorldId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get this node's 40-bit ZeroTier address
|
* Get this node's 40-bit ZeroTier address
|
||||||
*
|
*
|
||||||
@@ -1687,6 +1913,20 @@ int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage
|
|||||||
*/
|
*/
|
||||||
void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node);
|
void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a VERB_USER_MESSAGE to another ZeroTier node
|
||||||
|
*
|
||||||
|
* There is no delivery guarantee here. Failure can occur if the message is
|
||||||
|
* too large or if dest is not a valid ZeroTier address.
|
||||||
|
*
|
||||||
|
* @param dest Destination ZeroTier address
|
||||||
|
* @param typeId VERB_USER_MESSAGE type ID
|
||||||
|
* @param data Payload data to attach to user message
|
||||||
|
* @param len Length of data in bytes
|
||||||
|
* @return Boolean: non-zero on success, zero on failure
|
||||||
|
*/
|
||||||
|
int ZT_Node_sendUserMessage(ZT_Node *node,uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a network configuration master instance for this node
|
* Set a network configuration master instance for this node
|
||||||
*
|
*
|
||||||
@@ -1870,27 +2110,6 @@ void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs);
|
|||||||
*/
|
*/
|
||||||
void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
|
void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
|
||||||
|
|
||||||
/**
|
|
||||||
* Do things in the background until Node dies
|
|
||||||
*
|
|
||||||
* This function can be called from one or more background threads to process
|
|
||||||
* certain tasks in the background to improve foreground performance. It will
|
|
||||||
* not return until the Node is shut down. If threading is not enabled in
|
|
||||||
* this build it will return immediately and will do nothing.
|
|
||||||
*
|
|
||||||
* This is completely optional. If this is never called, all processing is
|
|
||||||
* done in the foreground in the various processXXXX() methods.
|
|
||||||
*
|
|
||||||
* This does NOT replace or eliminate the need to call the normal
|
|
||||||
* processBackgroundTasks() function in your main loop. This mechanism is
|
|
||||||
* used to offload the processing of expensive mssages onto background
|
|
||||||
* handler threads to prevent foreground performance degradation under
|
|
||||||
* high load.
|
|
||||||
*
|
|
||||||
* @param node Node instance
|
|
||||||
*/
|
|
||||||
void ZT_Node_backgroundThreadMain(ZT_Node *node);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get ZeroTier One version
|
* Get ZeroTier One version
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
CC=cc
|
|
||||||
CXX=c++
|
|
||||||
|
|
||||||
INCLUDES=
|
|
||||||
DEFS=
|
|
||||||
LIBS=
|
|
||||||
|
|
||||||
include objects.mk
|
|
||||||
OBJS+=osdep/BSDEthernetTap.o ext/lz4/lz4.o ext/json-parser/json.o ext/http-parser/http_parser.o
|
|
||||||
|
|
||||||
# "make official" is a shortcut for this
|
|
||||||
ifeq ($(ZT_OFFICIAL_RELEASE),1)
|
|
||||||
DEFS+=-DZT_OFFICIAL_RELEASE
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Build with ZT_ENABLE_CLUSTER=1 to build with cluster support
|
|
||||||
ifeq ($(ZT_ENABLE_CLUSTER),1)
|
|
||||||
DEFS+=-DZT_ENABLE_CLUSTER
|
|
||||||
endif
|
|
||||||
|
|
||||||
# "make debug" is a shortcut for this
|
|
||||||
ifeq ($(ZT_DEBUG),1)
|
|
||||||
DEFS+=-DZT_TRACE
|
|
||||||
CFLAGS+=-Wall -g -pthread $(INCLUDES) $(DEFS)
|
|
||||||
LDFLAGS+=
|
|
||||||
STRIP=echo
|
|
||||||
# The following line enables optimization for the crypto code, since
|
|
||||||
# C25519 in particular is almost UNUSABLE in heavy testing without it.
|
|
||||||
ext/lz4/lz4.o node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
|
|
||||||
else
|
|
||||||
CFLAGS?=-O3 -fstack-protector
|
|
||||||
CFLAGS+=-Wall -fPIE -fvisibility=hidden -fstack-protector -pthread $(INCLUDES) -DNDEBUG $(DEFS)
|
|
||||||
LDFLAGS+=-pie -Wl,-z,relro,-z,now
|
|
||||||
STRIP=strip --strip-all
|
|
||||||
endif
|
|
||||||
|
|
||||||
CXXFLAGS+=$(CFLAGS) -fno-rtti
|
|
||||||
|
|
||||||
all: one
|
|
||||||
|
|
||||||
one: $(OBJS) service/OneService.o one.o
|
|
||||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) service/OneService.o one.o $(LIBS)
|
|
||||||
$(STRIP) zerotier-one
|
|
||||||
ln -sf zerotier-one zerotier-idtool
|
|
||||||
ln -sf zerotier-one zerotier-cli
|
|
||||||
|
|
||||||
selftest: $(OBJS) selftest.o
|
|
||||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)
|
|
||||||
$(STRIP) zerotier-selftest
|
|
||||||
|
|
||||||
# No installer on FreeBSD yet
|
|
||||||
#installer: one FORCE
|
|
||||||
# ./buildinstaller.sh
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o build-* zerotier-one zerotier-idtool zerotier-selftest zerotier-cli ZeroTierOneInstaller-*
|
|
||||||
|
|
||||||
debug: FORCE
|
|
||||||
make -j 4 ZT_DEBUG=1
|
|
||||||
|
|
||||||
#official: FORCE
|
|
||||||
# make -j 4 ZT_OFFICIAL_RELEASE=1
|
|
||||||
# ./buildinstaller.sh
|
|
||||||
|
|
||||||
FORCE:
|
|
||||||
@@ -1,24 +1,3 @@
|
|||||||
#
|
|
||||||
# Makefile for ZeroTier One on Linux
|
|
||||||
#
|
|
||||||
# This is confirmed to work on distributions newer than CentOS 6 (the
|
|
||||||
# one used for reference builds) and on 32 and 64 bit x86 and ARM
|
|
||||||
# machines. It should also work on other 'normal' machines and recent
|
|
||||||
# distributions. Editing might be required for tiny devices or weird
|
|
||||||
# distros.
|
|
||||||
#
|
|
||||||
# Targets
|
|
||||||
# one: zerotier-one and symlinks (cli and idtool)
|
|
||||||
# manpages: builds manpages, requires 'ronn' or nodeJS (will use either)
|
|
||||||
# all: builds 'one' and 'manpages'
|
|
||||||
# selftest: zerotier-selftest
|
|
||||||
# debug: builds 'one' and 'selftest' with tracing and debug flags
|
|
||||||
# clean: removes all built files, objects, other trash
|
|
||||||
# distclean: removes a few other things that might be present
|
|
||||||
# debian: build DEB packages; deb dev tools must be present
|
|
||||||
# redhat: build RPM packages; rpm dev tools must be present
|
|
||||||
#
|
|
||||||
|
|
||||||
# Automagically pick clang or gcc, with preference for clang
|
# Automagically pick clang or gcc, with preference for clang
|
||||||
# This is only done if we have not overridden these with an environment or CLI variable
|
# This is only done if we have not overridden these with an environment or CLI variable
|
||||||
ifeq ($(origin CC),default)
|
ifeq ($(origin CC),default)
|
||||||
@@ -28,8 +7,6 @@ ifeq ($(origin CXX),default)
|
|||||||
CXX=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
|
CXX=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
#UNAME_M=$(shell $(CC) -dumpmachine | cut -d '-' -f 1)
|
|
||||||
|
|
||||||
INCLUDES?=
|
INCLUDES?=
|
||||||
DEFS?=-D_FORTIFY_SOURCE=2
|
DEFS?=-D_FORTIFY_SOURCE=2
|
||||||
LDLIBS?=
|
LDLIBS?=
|
||||||
@@ -37,33 +14,14 @@ DESTDIR?=
|
|||||||
|
|
||||||
include objects.mk
|
include objects.mk
|
||||||
|
|
||||||
# On Linux we auto-detect the presence of some libraries and if present we
|
# Use bundled http-parser since distribution versions are NOT API-stable or compatible!
|
||||||
# link against the system version. This works with our package build images.
|
# Trying to use dynamically linked libhttp-parser causes tons of compatibility problems.
|
||||||
ifeq ($(wildcard /usr/include/lz4.h),)
|
|
||||||
OBJS+=ext/lz4/lz4.o
|
|
||||||
else
|
|
||||||
LDLIBS+=-llz4
|
|
||||||
DEFS+=-DZT_USE_SYSTEM_LZ4
|
|
||||||
endif
|
|
||||||
ifeq ($(wildcard /usr/include/http_parser.h),)
|
|
||||||
OBJS+=ext/http-parser/http_parser.o
|
OBJS+=ext/http-parser/http_parser.o
|
||||||
else
|
|
||||||
LDLIBS+=-lhttp_parser
|
|
||||||
DEFS+=-DZT_USE_SYSTEM_HTTP_PARSER
|
|
||||||
endif
|
|
||||||
ifeq ($(wildcard /usr/include/json-parser/json.h),)
|
|
||||||
OBJS+=ext/json/json.o
|
|
||||||
else
|
|
||||||
LDLIBS+=-ljsonparser
|
|
||||||
DEFS+=-DZT_USE_SYSTEM_JSON_PARSER
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(ZT_USE_MINIUPNPC),1)
|
# Auto-detect miniupnpc and nat-pmp as well and use system libs if present,
|
||||||
|
# otherwise build into binary as done on Mac and Windows.
|
||||||
OBJS+=osdep/PortMapper.o
|
OBJS+=osdep/PortMapper.o
|
||||||
|
|
||||||
DEFS+=-DZT_USE_MINIUPNPC
|
DEFS+=-DZT_USE_MINIUPNPC
|
||||||
|
|
||||||
# Auto-detect libminiupnpc at least v2.0
|
|
||||||
MINIUPNPC_IS_NEW_ENOUGH=$(shell grep -sqr '.*define.*MINIUPNPC_VERSION.*"2.."' /usr/include/miniupnpc/miniupnpc.h && echo 1)
|
MINIUPNPC_IS_NEW_ENOUGH=$(shell grep -sqr '.*define.*MINIUPNPC_VERSION.*"2.."' /usr/include/miniupnpc/miniupnpc.h && echo 1)
|
||||||
ifeq ($(MINIUPNPC_IS_NEW_ENOUGH),1)
|
ifeq ($(MINIUPNPC_IS_NEW_ENOUGH),1)
|
||||||
DEFS+=-DZT_USE_SYSTEM_MINIUPNPC
|
DEFS+=-DZT_USE_SYSTEM_MINIUPNPC
|
||||||
@@ -72,45 +30,44 @@ ifeq ($(ZT_USE_MINIUPNPC),1)
|
|||||||
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
|
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
|
||||||
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
|
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
|
endif
|
||||||
|
|
||||||
# Auto-detect libnatpmp
|
|
||||||
ifeq ($(wildcard /usr/include/natpmp.h),)
|
ifeq ($(wildcard /usr/include/natpmp.h),)
|
||||||
OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o
|
OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o
|
||||||
else
|
else
|
||||||
LDLIBS+=-lnatpmp
|
LDLIBS+=-lnatpmp
|
||||||
DEFS+=-DZT_USE_SYSTEM_NATPMP
|
DEFS+=-DZT_USE_SYSTEM_NATPMP
|
||||||
endif
|
endif
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(ZT_ENABLE_NETWORK_CONTROLLER),1)
|
|
||||||
DEFS+=-DZT_ENABLE_NETWORK_CONTROLLER
|
|
||||||
LDLIBS+=-L/usr/local/lib -lsqlite3
|
|
||||||
OBJS+=controller/SqliteNetworkController.o
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(ZT_ENABLE_CLUSTER),1)
|
ifeq ($(ZT_ENABLE_CLUSTER),1)
|
||||||
DEFS+=-DZT_ENABLE_CLUSTER
|
DEFS+=-DZT_ENABLE_CLUSTER
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(ZT_SYNOLOGY), 1)
|
||||||
|
DEFS+=-D__SYNOLOGY__
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(ZT_TRACE),1)
|
ifeq ($(ZT_TRACE),1)
|
||||||
DEFS+=-DZT_TRACE
|
DEFS+=-DZT_TRACE
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(ZT_RULES_ENGINE_DEBUGGING),1)
|
||||||
|
DEFS+=-DZT_RULES_ENGINE_DEBUGGING
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(ZT_DEBUG),1)
|
ifeq ($(ZT_DEBUG),1)
|
||||||
DEFS+=-DZT_TRACE
|
DEFS+=-DZT_TRACE
|
||||||
override CFLAGS+=-Wall -g -O -pthread $(INCLUDES) $(DEFS)
|
override CFLAGS+=-Wall -g -O -pthread $(INCLUDES) $(DEFS)
|
||||||
override CXXFLAGS+=-Wall -g -O -pthread $(INCLUDES) $(DEFS)
|
override CXXFLAGS+=-Wall -g -O -std=c++11 -pthread $(INCLUDES) $(DEFS)
|
||||||
LDFLAGS=
|
override LDFLAGS+=
|
||||||
STRIP?=echo
|
STRIP?=echo
|
||||||
# The following line enables optimization for the crypto code, since
|
# The following line enables optimization for the crypto code, since
|
||||||
# C25519 in particular is almost UNUSABLE in -O0 even on a 3ghz box!
|
# C25519 in particular is almost UNUSABLE in -O0 even on a 3ghz box!
|
||||||
ext/lz4/lz4.o node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
|
node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
|
||||||
else
|
else
|
||||||
CFLAGS?=-O3 -fstack-protector-strong
|
CFLAGS?=-O3 -fstack-protector
|
||||||
override CFLAGS+=-Wall -fPIE -pthread $(INCLUDES) -DNDEBUG $(DEFS)
|
override CFLAGS+=-Wall -fPIE -pthread $(INCLUDES) -DNDEBUG $(DEFS)
|
||||||
CXXFLAGS?=-O3 -fstack-protector-strong
|
CXXFLAGS?=-O3 -fstack-protector
|
||||||
override CXXFLAGS+=-Wall -Wno-unused-result -Wreorder -fPIE -fno-rtti -pthread $(INCLUDES) -DNDEBUG $(DEFS)
|
override CXXFLAGS+=-Wall -Wno-unused-result -Wreorder -fPIE -std=c++11 -pthread $(INCLUDES) -DNDEBUG $(DEFS)
|
||||||
LDFLAGS=-pie -Wl,-z,relro,-z,now
|
override LDFLAGS+=-pie -Wl,-z,relro,-z,now
|
||||||
STRIP?=strip
|
STRIP?=strip
|
||||||
STRIP+=--strip-all
|
STRIP+=--strip-all
|
||||||
endif
|
endif
|
||||||
@@ -121,7 +78,38 @@ endif
|
|||||||
#LDFLAGS=
|
#LDFLAGS=
|
||||||
#STRIP=echo
|
#STRIP=echo
|
||||||
|
|
||||||
all: one manpages
|
# Determine system build architecture from compiler target
|
||||||
|
CC_MACH=$(shell $(CC) -dumpmachine | cut -d '-' -f 1)
|
||||||
|
ZT_ARCHITECTURE=0
|
||||||
|
ifeq ($(CC_MACH),x86_64)
|
||||||
|
ZT_ARCHITECTURE=2
|
||||||
|
endif
|
||||||
|
ifeq ($(CC_MACH),amd64)
|
||||||
|
ZT_ARCHITECTURE=2
|
||||||
|
endif
|
||||||
|
ifeq ($(CC_MACH),i386)
|
||||||
|
ZT_ARCHITECTURE=1
|
||||||
|
endif
|
||||||
|
ifeq ($(CC_MACH),i686)
|
||||||
|
ZT_ARCHITECTURE=1
|
||||||
|
endif
|
||||||
|
ifeq ($(CC_MACH),arm)
|
||||||
|
ZT_ARCHITECTURE=3
|
||||||
|
endif
|
||||||
|
ifeq ($(CC_MACH),arm64)
|
||||||
|
ZT_ARCHITECTURE=4
|
||||||
|
endif
|
||||||
|
ifeq ($(CC_MACH),aarch64)
|
||||||
|
ZT_ARCHITECTURE=4
|
||||||
|
endif
|
||||||
|
DEFS+=-DZT_BUILD_PLATFORM=1 -DZT_BUILD_ARCHITECTURE=$(ZT_ARCHITECTURE) -DZT_SOFTWARE_UPDATE_DEFAULT="\"disable\""
|
||||||
|
|
||||||
|
# Define this to build a static binary, which is needed to make this runnable on a few ancient Linux distros
|
||||||
|
ifeq ($(ZT_STATIC),1)
|
||||||
|
override LDFLAGS+=-static
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: one
|
||||||
|
|
||||||
one: $(OBJS) service/OneService.o one.o osdep/LinuxEthernetTap.o
|
one: $(OBJS) service/OneService.o one.o osdep/LinuxEthernetTap.o
|
||||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) service/OneService.o one.o osdep/LinuxEthernetTap.o $(LDLIBS)
|
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) service/OneService.o one.o osdep/LinuxEthernetTap.o $(LDLIBS)
|
||||||
@@ -139,13 +127,9 @@ manpages: FORCE
|
|||||||
doc: manpages
|
doc: manpages
|
||||||
|
|
||||||
clean: FORCE
|
clean: FORCE
|
||||||
rm -rf *.so *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o ext/miniupnpc/*.o ext/libnatpmp/*.o $(OBJS) zerotier-one zerotier-idtool zerotier-cli zerotier-selftest build-* ZeroTierOneInstaller-* *.deb *.rpm .depend doc/*.1 doc/*.2 doc/*.8 debian/files debian/zerotier-one*.debhelper debian/zerotier-one.substvars debian/*.log debian/zerotier-one
|
rm -rf *.so *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/miniupnpc/*.o ext/libnatpmp/*.o $(OBJS) zerotier-one zerotier-idtool zerotier-cli zerotier-selftest build-* ZeroTierOneInstaller-* *.deb *.rpm .depend debian/files debian/zerotier-one*.debhelper debian/zerotier-one.substvars debian/*.log debian/zerotier-one doc/node_modules
|
||||||
|
|
||||||
distclean: clean
|
distclean: clean
|
||||||
rm -rf doc/node_modules
|
|
||||||
find linux-build-farm -type f -name '*.deb' -print0 | xargs -0 rm -fv
|
|
||||||
find linux-build-farm -type f -name '*.rpm' -print0 | xargs -0 rm -fv
|
|
||||||
find linux-build-farm -type f -name 'zt1-src.tar.gz' | xargs rm -fv
|
|
||||||
|
|
||||||
realclean: distclean
|
realclean: distclean
|
||||||
|
|
||||||
|
|||||||
@@ -1,55 +1,45 @@
|
|||||||
ifeq ($(origin CC),default)
|
CC=clang
|
||||||
CC=$(shell if [ -e /usr/bin/clang ]; then echo clang; else echo gcc; fi)
|
CXX=clang++
|
||||||
endif
|
|
||||||
ifeq ($(origin CXX),default)
|
|
||||||
CXX=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
|
|
||||||
endif
|
|
||||||
|
|
||||||
INCLUDES=
|
INCLUDES=
|
||||||
DEFS=
|
DEFS=
|
||||||
LIBS=
|
LIBS=
|
||||||
ARCH_FLAGS=-arch x86_64
|
ARCH_FLAGS=
|
||||||
|
|
||||||
include objects.mk
|
|
||||||
OBJS+=osdep/OSXEthernetTap.o ext/lz4/lz4.o ext/json-parser/json.o ext/http-parser/http_parser.o
|
|
||||||
|
|
||||||
# Disable codesign since open source users will not have ZeroTier's certs
|
|
||||||
CODESIGN=echo
|
CODESIGN=echo
|
||||||
PRODUCTSIGN=echo
|
PRODUCTSIGN=echo
|
||||||
CODESIGN_APP_CERT=
|
CODESIGN_APP_CERT=
|
||||||
CODESIGN_INSTALLER_CERT=
|
CODESIGN_INSTALLER_CERT=
|
||||||
|
|
||||||
# Build with libminiupnpc by default for Mac -- desktops/laptops almost always want this
|
ZT_BUILD_PLATFORM=3
|
||||||
ZT_USE_MINIUPNPC?=1
|
ZT_BUILD_ARCHITECTURE=2
|
||||||
|
ZT_VERSION_MAJOR=$(shell cat version.h | grep -F VERSION_MAJOR | cut -d ' ' -f 3)
|
||||||
|
ZT_VERSION_MINOR=$(shell cat version.h | grep -F VERSION_MINOR | cut -d ' ' -f 3)
|
||||||
|
ZT_VERSION_REV=$(shell cat version.h | grep -F VERSION_REVISION | cut -d ' ' -f 3)
|
||||||
|
ZT_VERSION_BUILD=$(shell cat version.h | grep -F VERSION_BUILD | cut -d ' ' -f 3)
|
||||||
|
|
||||||
# For internal use only -- signs everything with ZeroTier's developer cert
|
DEFS+=-DZT_BUILD_PLATFORM=$(ZT_BUILD_PLATFORM) -DZT_BUILD_ARCHITECTURE=$(ZT_BUILD_ARCHITECTURE)
|
||||||
|
|
||||||
|
include objects.mk
|
||||||
|
OBJS+=osdep/OSXEthernetTap.o ext/http-parser/http_parser.o
|
||||||
|
|
||||||
|
# Official releases are signed with our Apple cert and apply software updates by default
|
||||||
ifeq ($(ZT_OFFICIAL_RELEASE),1)
|
ifeq ($(ZT_OFFICIAL_RELEASE),1)
|
||||||
DEFS+=-DZT_OFFICIAL_RELEASE -DZT_AUTO_UPDATE
|
DEFS+=-DZT_SOFTWARE_UPDATE_DEFAULT="\"apply\""
|
||||||
ZT_USE_MINIUPNPC=1
|
ZT_USE_MINIUPNPC=1
|
||||||
CODESIGN=codesign
|
CODESIGN=codesign
|
||||||
PRODUCTSIGN=productsign
|
PRODUCTSIGN=productsign
|
||||||
CODESIGN_APP_CERT="Developer ID Application: ZeroTier Networks LLC (8ZD9JUCZ4V)"
|
CODESIGN_APP_CERT="Developer ID Application: ZeroTier, Inc (8ZD9JUCZ4V)"
|
||||||
CODESIGN_INSTALLER_CERT="Developer ID Installer: ZeroTier Networks LLC (8ZD9JUCZ4V)"
|
CODESIGN_INSTALLER_CERT="Developer ID Installer: ZeroTier, Inc (8ZD9JUCZ4V)"
|
||||||
|
else
|
||||||
|
DEFS+=-DZT_SOFTWARE_UPDATE_DEFAULT="\"download\""
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ZT_ENABLE_CLUSTER),1)
|
ifeq ($(ZT_ENABLE_CLUSTER),1)
|
||||||
DEFS+=-DZT_ENABLE_CLUSTER
|
DEFS+=-DZT_ENABLE_CLUSTER
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ZT_AUTO_UPDATE),1)
|
# Build miniupnpc and nat-pmp as included libraries -- extra defs are required for these sources
|
||||||
DEFS+=-DZT_AUTO_UPDATE
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(ZT_USE_MINIUPNPC),1)
|
|
||||||
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_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
|
||||||
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
|
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
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(ZT_ENABLE_NETWORK_CONTROLLER),1)
|
|
||||||
DEFS+=-DZT_ENABLE_NETWORK_CONTROLLER
|
|
||||||
LIBS+=-L/usr/local/lib -lsqlite3
|
|
||||||
OBJS+=controller/SqliteNetworkController.o
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Debug mode -- dump trace output, build binary with -g
|
# Debug mode -- dump trace output, build binary with -g
|
||||||
ifeq ($(ZT_DEBUG),1)
|
ifeq ($(ZT_DEBUG),1)
|
||||||
@@ -58,16 +48,16 @@ ifeq ($(ZT_DEBUG),1)
|
|||||||
STRIP=echo
|
STRIP=echo
|
||||||
# The following line enables optimization for the crypto code, since
|
# The following line enables optimization for the crypto code, since
|
||||||
# C25519 in particular is almost UNUSABLE in heavy testing without it.
|
# C25519 in particular is almost UNUSABLE in heavy testing without it.
|
||||||
ext/lz4/lz4.o node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
|
node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
|
||||||
else
|
else
|
||||||
CFLAGS?=-Ofast -fstack-protector-strong
|
CFLAGS?=-Ofast -fstack-protector-strong
|
||||||
CFLAGS+=$(ARCH_FLAGS) -Wall -flto -fPIE -pthread -mmacosx-version-min=10.7 -DNDEBUG -Wno-unused-private-field $(INCLUDES) $(DEFS)
|
CFLAGS+=$(ARCH_FLAGS) -Wall -flto -fPIE -pthread -mmacosx-version-min=10.7 -DNDEBUG -Wno-unused-private-field $(INCLUDES) $(DEFS)
|
||||||
STRIP=strip
|
STRIP=strip
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CXXFLAGS=$(CFLAGS) -fno-rtti
|
CXXFLAGS=$(CFLAGS) -std=c++11 -stdlib=libc++
|
||||||
|
|
||||||
all: one
|
all: one macui
|
||||||
|
|
||||||
one: $(OBJS) service/OneService.o one.o
|
one: $(OBJS) service/OneService.o one.o
|
||||||
$(CXX) $(CXXFLAGS) -o zerotier-one $(OBJS) service/OneService.o one.o $(LIBS)
|
$(CXX) $(CXXFLAGS) -o zerotier-one $(OBJS) service/OneService.o one.o $(LIBS)
|
||||||
@@ -75,11 +65,14 @@ one: $(OBJS) service/OneService.o one.o
|
|||||||
ln -sf zerotier-one zerotier-idtool
|
ln -sf zerotier-one zerotier-idtool
|
||||||
ln -sf zerotier-one zerotier-cli
|
ln -sf zerotier-one zerotier-cli
|
||||||
$(CODESIGN) -f -s $(CODESIGN_APP_CERT) zerotier-one
|
$(CODESIGN) -f -s $(CODESIGN_APP_CERT) zerotier-one
|
||||||
$(CODESIGN) -vvv zerotier-one
|
|
||||||
|
|
||||||
cli: FORCE
|
macui: FORCE
|
||||||
$(CXX) -Os -mmacosx-version-min=10.7 -std=c++11 -stdlib=libc++ -o zerotier cli/zerotier.cpp osdep/OSUtils.cpp node/InetAddress.cpp node/Utils.cpp node/Salsa20.cpp node/Identity.cpp node/SHA512.cpp node/C25519.cpp -lcurl
|
cd macui && xcodebuild -target "ZeroTier One" -configuration Release
|
||||||
$(STRIP) zerotier
|
$(CODESIGN) -f -s $(CODESIGN_APP_CERT) "macui/build/Release/ZeroTier One.app"
|
||||||
|
|
||||||
|
#cli: FORCE
|
||||||
|
# $(CXX) $(CXXFLAGS) -o zerotier cli/zerotier.cpp osdep/OSUtils.cpp node/InetAddress.cpp node/Utils.cpp node/Salsa20.cpp node/Identity.cpp node/SHA512.cpp node/C25519.cpp -lcurl
|
||||||
|
# $(STRIP) zerotier
|
||||||
|
|
||||||
selftest: $(OBJS) selftest.o
|
selftest: $(OBJS) selftest.o
|
||||||
$(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)
|
$(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)
|
||||||
@@ -91,18 +84,22 @@ mac-dist-pkg: FORCE
|
|||||||
rm -f "ZeroTier One Signed.pkg"
|
rm -f "ZeroTier One Signed.pkg"
|
||||||
$(PRODUCTSIGN) --sign $(CODESIGN_INSTALLER_CERT) "ZeroTier One.pkg" "ZeroTier One Signed.pkg"
|
$(PRODUCTSIGN) --sign $(CODESIGN_INSTALLER_CERT) "ZeroTier One.pkg" "ZeroTier One Signed.pkg"
|
||||||
if [ -f "ZeroTier One Signed.pkg" ]; then mv -f "ZeroTier One Signed.pkg" "ZeroTier One.pkg"; fi
|
if [ -f "ZeroTier One Signed.pkg" ]; then mv -f "ZeroTier One Signed.pkg" "ZeroTier One.pkg"; fi
|
||||||
|
rm -f zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_*
|
||||||
|
cat ext/installfiles/mac-update/updater.tmpl.sh "ZeroTier One.pkg" >zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_$(ZT_VERSION_MAJOR).$(ZT_VERSION_MINOR).$(ZT_VERSION_REV)_$(ZT_VERSION_BUILD)
|
||||||
|
|
||||||
# For ZeroTier, Inc. to build official signed packages
|
# For ZeroTier, Inc. to build official signed packages
|
||||||
official: FORCE
|
official: FORCE
|
||||||
make clean
|
make clean
|
||||||
make ZT_OFFICIAL_RELEASE=1 -j 4 one
|
make ZT_OFFICIAL_RELEASE=1 -j 4 one
|
||||||
|
make ZT_OFFICIAL_RELEASE=1 macui
|
||||||
make ZT_OFFICIAL_RELEASE=1 mac-dist-pkg
|
make ZT_OFFICIAL_RELEASE=1 mac-dist-pkg
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o $(OBJS) zerotier-one zerotier-idtool zerotier-selftest zerotier-cli zerotier ZeroTierOneInstaller-* mkworld doc/node_modules
|
rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o $(OBJS) zerotier-one zerotier-idtool zerotier-selftest zerotier-cli zerotier mkworld doc/node_modules macui/build zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_*
|
||||||
|
|
||||||
distclean: clean
|
distclean: clean
|
||||||
rm -rf doc/node_modules
|
|
||||||
|
realclean: clean
|
||||||
|
|
||||||
# For those building from source -- installs signed binary tap driver in system ZT home
|
# For those building from source -- installs signed binary tap driver in system ZT home
|
||||||
install-mac-tap: FORCE
|
install-mac-tap: FORCE
|
||||||
|
|||||||
@@ -38,57 +38,26 @@ namespace ZeroTier {
|
|||||||
class Address
|
class Address
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Address()
|
Address() : _a(0) {}
|
||||||
throw() :
|
Address(const Address &a) : _a(a._a) {}
|
||||||
_a(0)
|
Address(uint64_t a) : _a(a & 0xffffffffffULL) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Address(const Address &a)
|
|
||||||
throw() :
|
|
||||||
_a(a._a)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Address(uint64_t a)
|
|
||||||
throw() :
|
|
||||||
_a(a & 0xffffffffffULL)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Address(const char *s)
|
|
||||||
throw()
|
|
||||||
{
|
|
||||||
unsigned char foo[ZT_ADDRESS_LENGTH];
|
|
||||||
setTo(foo,Utils::unhex(s,foo,ZT_ADDRESS_LENGTH));
|
|
||||||
}
|
|
||||||
|
|
||||||
Address(const std::string &s)
|
|
||||||
throw()
|
|
||||||
{
|
|
||||||
unsigned char foo[ZT_ADDRESS_LENGTH];
|
|
||||||
setTo(foo,Utils::unhex(s.c_str(),foo,ZT_ADDRESS_LENGTH));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param bits Raw address -- 5 bytes, big-endian byte order
|
* @param bits Raw address -- 5 bytes, big-endian byte order
|
||||||
* @param len Length of array
|
* @param len Length of array
|
||||||
*/
|
*/
|
||||||
Address(const void *bits,unsigned int len)
|
Address(const void *bits,unsigned int len)
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
setTo(bits,len);
|
setTo(bits,len);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Address &operator=(const Address &a)
|
inline Address &operator=(const Address &a)
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
_a = a._a;
|
_a = a._a;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Address &operator=(const uint64_t a)
|
inline Address &operator=(const uint64_t a)
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
_a = (a & 0xffffffffffULL);
|
_a = (a & 0xffffffffffULL);
|
||||||
return *this;
|
return *this;
|
||||||
@@ -99,7 +68,6 @@ public:
|
|||||||
* @param len Length of array
|
* @param len Length of array
|
||||||
*/
|
*/
|
||||||
inline void setTo(const void *bits,unsigned int len)
|
inline void setTo(const void *bits,unsigned int len)
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
if (len < ZT_ADDRESS_LENGTH) {
|
if (len < ZT_ADDRESS_LENGTH) {
|
||||||
_a = 0;
|
_a = 0;
|
||||||
@@ -119,7 +87,6 @@ public:
|
|||||||
* @param len Length of array
|
* @param len Length of array
|
||||||
*/
|
*/
|
||||||
inline void copyTo(void *bits,unsigned int len) const
|
inline void copyTo(void *bits,unsigned int len) const
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
if (len < ZT_ADDRESS_LENGTH)
|
if (len < ZT_ADDRESS_LENGTH)
|
||||||
return;
|
return;
|
||||||
@@ -138,7 +105,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
template<unsigned int C>
|
template<unsigned int C>
|
||||||
inline void appendTo(Buffer<C> &b) const
|
inline void appendTo(Buffer<C> &b) const
|
||||||
throw(std::out_of_range)
|
|
||||||
{
|
{
|
||||||
unsigned char *p = (unsigned char *)b.appendField(ZT_ADDRESS_LENGTH);
|
unsigned char *p = (unsigned char *)b.appendField(ZT_ADDRESS_LENGTH);
|
||||||
*(p++) = (unsigned char)((_a >> 32) & 0xff);
|
*(p++) = (unsigned char)((_a >> 32) & 0xff);
|
||||||
@@ -152,7 +118,6 @@ public:
|
|||||||
* @return Integer containing address (0 to 2^40)
|
* @return Integer containing address (0 to 2^40)
|
||||||
*/
|
*/
|
||||||
inline uint64_t toInt() const
|
inline uint64_t toInt() const
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
return _a;
|
return _a;
|
||||||
}
|
}
|
||||||
@@ -161,7 +126,6 @@ public:
|
|||||||
* @return Hash code for use with Hashtable
|
* @return Hash code for use with Hashtable
|
||||||
*/
|
*/
|
||||||
inline unsigned long hashCode() const
|
inline unsigned long hashCode() const
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
return (unsigned long)_a;
|
return (unsigned long)_a;
|
||||||
}
|
}
|
||||||
@@ -188,12 +152,12 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @return True if this address is not zero
|
* @return True if this address is not zero
|
||||||
*/
|
*/
|
||||||
inline operator bool() const throw() { return (_a != 0); }
|
inline operator bool() const { return (_a != 0); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to null/zero
|
* Set to null/zero
|
||||||
*/
|
*/
|
||||||
inline void zero() throw() { _a = 0; }
|
inline void zero() { _a = 0; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this address is reserved
|
* Check if this address is reserved
|
||||||
@@ -205,7 +169,6 @@ public:
|
|||||||
* @return True if address is reserved and may not be used
|
* @return True if address is reserved and may not be used
|
||||||
*/
|
*/
|
||||||
inline bool isReserved() const
|
inline bool isReserved() const
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
return ((!_a)||((_a >> 32) == ZT_ADDRESS_RESERVED_PREFIX));
|
return ((!_a)||((_a >> 32) == ZT_ADDRESS_RESERVED_PREFIX));
|
||||||
}
|
}
|
||||||
@@ -214,21 +177,21 @@ public:
|
|||||||
* @param i Value from 0 to 4 (inclusive)
|
* @param i Value from 0 to 4 (inclusive)
|
||||||
* @return Byte at said position (address interpreted in big-endian order)
|
* @return Byte at said position (address interpreted in big-endian order)
|
||||||
*/
|
*/
|
||||||
inline unsigned char operator[](unsigned int i) const throw() { return (unsigned char)((_a >> (32 - (i * 8))) & 0xff); }
|
inline unsigned char operator[](unsigned int i) const { return (unsigned char)((_a >> (32 - (i * 8))) & 0xff); }
|
||||||
|
|
||||||
inline bool operator==(const uint64_t &a) const throw() { return (_a == (a & 0xffffffffffULL)); }
|
inline bool operator==(const uint64_t &a) const { return (_a == (a & 0xffffffffffULL)); }
|
||||||
inline bool operator!=(const uint64_t &a) const throw() { return (_a != (a & 0xffffffffffULL)); }
|
inline bool operator!=(const uint64_t &a) const { return (_a != (a & 0xffffffffffULL)); }
|
||||||
inline bool operator>(const uint64_t &a) const throw() { return (_a > (a & 0xffffffffffULL)); }
|
inline bool operator>(const uint64_t &a) const { return (_a > (a & 0xffffffffffULL)); }
|
||||||
inline bool operator<(const uint64_t &a) const throw() { return (_a < (a & 0xffffffffffULL)); }
|
inline bool operator<(const uint64_t &a) const { return (_a < (a & 0xffffffffffULL)); }
|
||||||
inline bool operator>=(const uint64_t &a) const throw() { return (_a >= (a & 0xffffffffffULL)); }
|
inline bool operator>=(const uint64_t &a) const { return (_a >= (a & 0xffffffffffULL)); }
|
||||||
inline bool operator<=(const uint64_t &a) const throw() { return (_a <= (a & 0xffffffffffULL)); }
|
inline bool operator<=(const uint64_t &a) const { return (_a <= (a & 0xffffffffffULL)); }
|
||||||
|
|
||||||
inline bool operator==(const Address &a) const throw() { return (_a == a._a); }
|
inline bool operator==(const Address &a) const { return (_a == a._a); }
|
||||||
inline bool operator!=(const Address &a) const throw() { return (_a != a._a); }
|
inline bool operator!=(const Address &a) const { return (_a != a._a); }
|
||||||
inline bool operator>(const Address &a) const throw() { return (_a > a._a); }
|
inline bool operator>(const Address &a) const { return (_a > a._a); }
|
||||||
inline bool operator<(const Address &a) const throw() { return (_a < a._a); }
|
inline bool operator<(const Address &a) const { return (_a < a._a); }
|
||||||
inline bool operator>=(const Address &a) const throw() { return (_a >= a._a); }
|
inline bool operator>=(const Address &a) const { return (_a >= a._a); }
|
||||||
inline bool operator<=(const Address &a) const throw() { return (_a <= a._a); }
|
inline bool operator<=(const Address &a) const { return (_a <= a._a); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t _a;
|
uint64_t _a;
|
||||||
|
|||||||
@@ -20,11 +20,9 @@
|
|||||||
#define ZT_ATOMICCOUNTER_HPP
|
#define ZT_ATOMICCOUNTER_HPP
|
||||||
|
|
||||||
#include "Constants.hpp"
|
#include "Constants.hpp"
|
||||||
#include "Mutex.hpp"
|
|
||||||
#include "NonCopyable.hpp"
|
#include "NonCopyable.hpp"
|
||||||
|
|
||||||
#ifdef __WINDOWS__
|
#ifndef __GNUC__
|
||||||
// <atomic> will replace this whole class eventually once it's ubiquitous
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -36,75 +34,34 @@ namespace ZeroTier {
|
|||||||
class AtomicCounter : NonCopyable
|
class AtomicCounter : NonCopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* Initialize counter at zero
|
|
||||||
*/
|
|
||||||
AtomicCounter()
|
AtomicCounter()
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
_v = 0;
|
_v = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline operator int() const
|
|
||||||
throw()
|
|
||||||
{
|
|
||||||
#ifdef __GNUC__
|
|
||||||
return __sync_or_and_fetch(const_cast <volatile int *>(&_v),0);
|
|
||||||
#else
|
|
||||||
#ifdef __WINDOWS__
|
|
||||||
return (int)_v;
|
|
||||||
#else
|
|
||||||
_l.lock();
|
|
||||||
int v = _v;
|
|
||||||
_l.unlock();
|
|
||||||
return v;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int operator++()
|
inline int operator++()
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
return __sync_add_and_fetch(&_v,1);
|
return __sync_add_and_fetch(&_v,1);
|
||||||
#else
|
#else
|
||||||
#ifdef __WINDOWS__
|
|
||||||
return ++_v;
|
return ++_v;
|
||||||
#else
|
|
||||||
_l.lock();
|
|
||||||
int v = ++_v;
|
|
||||||
_l.unlock();
|
|
||||||
return v;
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int operator--()
|
inline int operator--()
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
return __sync_sub_and_fetch(&_v,1);
|
return __sync_sub_and_fetch(&_v,1);
|
||||||
#else
|
#else
|
||||||
#ifdef __WINDOWS__
|
|
||||||
return --_v;
|
return --_v;
|
||||||
#else
|
|
||||||
_l.lock();
|
|
||||||
int v = --_v;
|
|
||||||
_l.unlock();
|
|
||||||
return v;
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifdef __WINDOWS__
|
#ifdef __GNUC__
|
||||||
std::atomic_int _v;
|
|
||||||
#else
|
|
||||||
int _v;
|
int _v;
|
||||||
#ifndef __GNUC__
|
#else
|
||||||
#warning Neither __WINDOWS__ nor __GNUC__ so AtomicCounter using Mutex
|
std::atomic_int _v;
|
||||||
Mutex _l;
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ZT_BINARYSEMAPHORE_HPP
|
|
||||||
#define ZT_BINARYSEMAPHORE_HPP
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "Constants.hpp"
|
|
||||||
#include "NonCopyable.hpp"
|
|
||||||
|
|
||||||
#ifdef __WINDOWS__
|
|
||||||
|
|
||||||
#include <Windows.h>
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
class BinarySemaphore : NonCopyable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BinarySemaphore() throw() { _sem = CreateSemaphore(NULL,0,1,NULL); }
|
|
||||||
~BinarySemaphore() { CloseHandle(_sem); }
|
|
||||||
inline void wait() { WaitForSingleObject(_sem,INFINITE); }
|
|
||||||
inline void post() { ReleaseSemaphore(_sem,1,NULL); }
|
|
||||||
private:
|
|
||||||
HANDLE _sem;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
|
|
||||||
#else // !__WINDOWS__
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
class BinarySemaphore : NonCopyable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BinarySemaphore()
|
|
||||||
{
|
|
||||||
pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0);
|
|
||||||
pthread_cond_init(&_cond,(const pthread_condattr_t *)0);
|
|
||||||
_f = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
~BinarySemaphore()
|
|
||||||
{
|
|
||||||
pthread_cond_destroy(&_cond);
|
|
||||||
pthread_mutex_destroy(&_mh);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void wait()
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(const_cast <pthread_mutex_t *>(&_mh));
|
|
||||||
while (!_f)
|
|
||||||
pthread_cond_wait(const_cast <pthread_cond_t *>(&_cond),const_cast <pthread_mutex_t *>(&_mh));
|
|
||||||
_f = false;
|
|
||||||
pthread_mutex_unlock(const_cast <pthread_mutex_t *>(&_mh));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void post()
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(const_cast <pthread_mutex_t *>(&_mh));
|
|
||||||
_f = true;
|
|
||||||
pthread_mutex_unlock(const_cast <pthread_mutex_t *>(&_mh));
|
|
||||||
pthread_cond_signal(const_cast <pthread_cond_t *>(&_cond));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
pthread_cond_t _cond;
|
|
||||||
pthread_mutex_t _mh;
|
|
||||||
volatile bool _f;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
|
|
||||||
#endif // !__WINDOWS__
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -61,11 +61,11 @@ public:
|
|||||||
// STL container idioms
|
// STL container idioms
|
||||||
typedef unsigned char value_type;
|
typedef unsigned char value_type;
|
||||||
typedef unsigned char * pointer;
|
typedef unsigned char * pointer;
|
||||||
typedef const unsigned char * const_pointer;
|
typedef const char * const_pointer;
|
||||||
typedef unsigned char & reference;
|
typedef char & reference;
|
||||||
typedef const unsigned char & const_reference;
|
typedef const char & const_reference;
|
||||||
typedef unsigned char * iterator;
|
typedef char * iterator;
|
||||||
typedef const unsigned char * const_iterator;
|
typedef const char * const_iterator;
|
||||||
typedef unsigned int size_type;
|
typedef unsigned int size_type;
|
||||||
typedef int difference_type;
|
typedef int difference_type;
|
||||||
typedef std::reverse_iterator<iterator> reverse_iterator;
|
typedef std::reverse_iterator<iterator> reverse_iterator;
|
||||||
@@ -79,8 +79,7 @@ public:
|
|||||||
inline const_reverse_iterator rbegin() const { return const_reverse_iterator(begin()); }
|
inline const_reverse_iterator rbegin() const { return const_reverse_iterator(begin()); }
|
||||||
inline const_reverse_iterator rend() const { return const_reverse_iterator(end()); }
|
inline const_reverse_iterator rend() const { return const_reverse_iterator(end()); }
|
||||||
|
|
||||||
Buffer()
|
Buffer() :
|
||||||
throw() :
|
|
||||||
_l(0)
|
_l(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -419,87 +418,70 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Set buffer data length to zero
|
* Set buffer data length to zero
|
||||||
*/
|
*/
|
||||||
inline void clear()
|
inline void clear() { _l = 0; }
|
||||||
throw()
|
|
||||||
{
|
|
||||||
_l = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zero buffer up to size()
|
* Zero buffer up to size()
|
||||||
*/
|
*/
|
||||||
inline void zero()
|
inline void zero() { memset(_b,0,_l); }
|
||||||
throw()
|
|
||||||
{
|
|
||||||
memset(_b,0,_l);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zero unused capacity area
|
* Zero unused capacity area
|
||||||
*/
|
*/
|
||||||
inline void zeroUnused()
|
inline void zeroUnused() { memset(_b + _l,0,C - _l); }
|
||||||
throw()
|
|
||||||
{
|
|
||||||
memset(_b + _l,0,C - _l);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unconditionally and securely zero buffer's underlying memory
|
* Unconditionally and securely zero buffer's underlying memory
|
||||||
*/
|
*/
|
||||||
inline void burn()
|
inline void burn() { Utils::burn(_b,sizeof(_b)); }
|
||||||
throw()
|
|
||||||
{
|
|
||||||
Utils::burn(_b,sizeof(_b));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Constant pointer to data in buffer
|
* @return Constant pointer to data in buffer
|
||||||
*/
|
*/
|
||||||
inline const void *data() const throw() { return _b; }
|
inline const void *data() const { return _b; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Non-constant pointer to data in buffer
|
||||||
|
*/
|
||||||
|
inline void *unsafeData() { return _b; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Size of data in buffer
|
* @return Size of data in buffer
|
||||||
*/
|
*/
|
||||||
inline unsigned int size() const throw() { return _l; }
|
inline unsigned int size() const { return _l; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Capacity of buffer
|
* @return Capacity of buffer
|
||||||
*/
|
*/
|
||||||
inline unsigned int capacity() const throw() { return C; }
|
inline unsigned int capacity() const { return C; }
|
||||||
|
|
||||||
template<unsigned int C2>
|
template<unsigned int C2>
|
||||||
inline bool operator==(const Buffer<C2> &b) const
|
inline bool operator==(const Buffer<C2> &b) const
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
return ((_l == b._l)&&(!memcmp(_b,b._b,_l)));
|
return ((_l == b._l)&&(!memcmp(_b,b._b,_l)));
|
||||||
}
|
}
|
||||||
template<unsigned int C2>
|
template<unsigned int C2>
|
||||||
inline bool operator!=(const Buffer<C2> &b) const
|
inline bool operator!=(const Buffer<C2> &b) const
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
return ((_l != b._l)||(memcmp(_b,b._b,_l)));
|
return ((_l != b._l)||(memcmp(_b,b._b,_l)));
|
||||||
}
|
}
|
||||||
template<unsigned int C2>
|
template<unsigned int C2>
|
||||||
inline bool operator<(const Buffer<C2> &b) const
|
inline bool operator<(const Buffer<C2> &b) const
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
return (memcmp(_b,b._b,std::min(_l,b._l)) < 0);
|
return (memcmp(_b,b._b,std::min(_l,b._l)) < 0);
|
||||||
}
|
}
|
||||||
template<unsigned int C2>
|
template<unsigned int C2>
|
||||||
inline bool operator>(const Buffer<C2> &b) const
|
inline bool operator>(const Buffer<C2> &b) const
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
return (b < *this);
|
return (b < *this);
|
||||||
}
|
}
|
||||||
template<unsigned int C2>
|
template<unsigned int C2>
|
||||||
inline bool operator<=(const Buffer<C2> &b) const
|
inline bool operator<=(const Buffer<C2> &b) const
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
return !(b < *this);
|
return !(b < *this);
|
||||||
}
|
}
|
||||||
template<unsigned int C2>
|
template<unsigned int C2>
|
||||||
inline bool operator>=(const Buffer<C2> &b) const
|
inline bool operator>=(const Buffer<C2> &b) const
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
return !(*this < b);
|
return !(*this < b);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "CertificateOfMembership.hpp"
|
#include "CertificateOfMembership.hpp"
|
||||||
|
#include "RuntimeEnvironment.hpp"
|
||||||
|
#include "Topology.hpp"
|
||||||
|
#include "Switch.hpp"
|
||||||
|
#include "Network.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
@@ -152,6 +156,9 @@ bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) c
|
|||||||
unsigned int myidx = 0;
|
unsigned int myidx = 0;
|
||||||
unsigned int otheridx = 0;
|
unsigned int otheridx = 0;
|
||||||
|
|
||||||
|
if ((_qualifierCount == 0)||(other._qualifierCount == 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
while (myidx < _qualifierCount) {
|
while (myidx < _qualifierCount) {
|
||||||
// Fail if we're at the end of other, since this means the field is
|
// Fail if we're at the end of other, since this means the field is
|
||||||
// missing.
|
// missing.
|
||||||
@@ -182,7 +189,7 @@ bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) c
|
|||||||
|
|
||||||
bool CertificateOfMembership::sign(const Identity &with)
|
bool CertificateOfMembership::sign(const Identity &with)
|
||||||
{
|
{
|
||||||
uint64_t *const buf = new uint64_t[_qualifierCount * 3];
|
uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
|
||||||
unsigned int ptr = 0;
|
unsigned int ptr = 0;
|
||||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||||
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
||||||
@@ -193,38 +200,32 @@ bool CertificateOfMembership::sign(const Identity &with)
|
|||||||
try {
|
try {
|
||||||
_signature = with.sign(buf,ptr * sizeof(uint64_t));
|
_signature = with.sign(buf,ptr * sizeof(uint64_t));
|
||||||
_signedBy = with.address();
|
_signedBy = with.address();
|
||||||
delete [] buf;
|
|
||||||
return true;
|
return true;
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
_signedBy.zero();
|
_signedBy.zero();
|
||||||
delete [] buf;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CertificateOfMembership::verify(const Identity &id) const
|
int CertificateOfMembership::verify(const RuntimeEnvironment *RR) const
|
||||||
{
|
{
|
||||||
if (!_signedBy)
|
if ((!_signedBy)||(_signedBy != Network::controllerFor(networkId()))||(_qualifierCount > ZT_NETWORK_COM_MAX_QUALIFIERS))
|
||||||
return false;
|
return -1;
|
||||||
if (id.address() != _signedBy)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint64_t *const buf = new uint64_t[_qualifierCount * 3];
|
const Identity id(RR->topology->getIdentity(_signedBy));
|
||||||
|
if (!id) {
|
||||||
|
RR->sw->requestWhois(_signedBy);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
|
||||||
unsigned int ptr = 0;
|
unsigned int ptr = 0;
|
||||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||||
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
||||||
buf[ptr++] = Utils::hton(_qualifiers[i].value);
|
buf[ptr++] = Utils::hton(_qualifiers[i].value);
|
||||||
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
|
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
|
||||||
}
|
}
|
||||||
|
return (id.verify(buf,ptr * sizeof(uint64_t),_signature) ? 0 : -1);
|
||||||
bool valid = false;
|
|
||||||
try {
|
|
||||||
valid = id.verify(buf,ptr * sizeof(uint64_t),_signature);
|
|
||||||
delete [] buf;
|
|
||||||
} catch ( ... ) {
|
|
||||||
delete [] buf;
|
|
||||||
}
|
|
||||||
return valid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -34,22 +34,14 @@
|
|||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default window of time for certificate agreement
|
* Maximum number of qualifiers allowed in a COM (absolute max: 65535)
|
||||||
*
|
|
||||||
* Right now we use time for 'revision' so this is the maximum time divergence
|
|
||||||
* between two certs for them to agree. It comes out to five minutes, which
|
|
||||||
* gives a lot of margin for error if the controller hiccups or its clock
|
|
||||||
* drifts but causes de-authorized peers to fall off fast enough.
|
|
||||||
*/
|
*/
|
||||||
#define ZT_NETWORK_COM_DEFAULT_REVISION_MAX_DELTA (ZT_NETWORK_AUTOCONF_DELAY * 5)
|
#define ZT_NETWORK_COM_MAX_QUALIFIERS 8
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of qualifiers in a COM
|
|
||||||
*/
|
|
||||||
#define ZT_NETWORK_COM_MAX_QUALIFIERS 16
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
class RuntimeEnvironment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Certificate of network membership
|
* Certificate of network membership
|
||||||
*
|
*
|
||||||
@@ -79,22 +71,11 @@ namespace ZeroTier {
|
|||||||
class CertificateOfMembership
|
class CertificateOfMembership
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* Certificate type codes, used in serialization
|
|
||||||
*
|
|
||||||
* Only one so far, and only one hopefully there shall be for quite some
|
|
||||||
* time.
|
|
||||||
*/
|
|
||||||
enum Type
|
|
||||||
{
|
|
||||||
COM_UINT64_ED25519 = 1 // tuples of unsigned 64's signed with Ed25519
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reserved qualifier IDs
|
* Reserved qualifier IDs
|
||||||
*
|
*
|
||||||
* IDs below 65536 should be considered reserved for future global
|
* IDs below 1024 are reserved for use as standard IDs. Others are available
|
||||||
* assignment here.
|
* for user-defined use.
|
||||||
*
|
*
|
||||||
* Addition of new required fields requires that code in hasRequiredFields
|
* Addition of new required fields requires that code in hasRequiredFields
|
||||||
* be updated as well.
|
* be updated as well.
|
||||||
@@ -102,36 +83,27 @@ public:
|
|||||||
enum ReservedId
|
enum ReservedId
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Revision number of certificate
|
* Timestamp of certificate
|
||||||
*
|
|
||||||
* Certificates may differ in revision number by a designated max
|
|
||||||
* delta. Differences wider than this cause certificates not to agree.
|
|
||||||
*/
|
*/
|
||||||
COM_RESERVED_ID_REVISION = 0,
|
COM_RESERVED_ID_TIMESTAMP = 0,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Network ID for which certificate was issued
|
* Network ID for which certificate was issued
|
||||||
*
|
|
||||||
* maxDelta here is zero, since this must match.
|
|
||||||
*/
|
*/
|
||||||
COM_RESERVED_ID_NETWORK_ID = 1,
|
COM_RESERVED_ID_NETWORK_ID = 1,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ZeroTier address to whom certificate was issued
|
* ZeroTier address to whom certificate was issued
|
||||||
*
|
|
||||||
* maxDelta will be 0xffffffffffffffff here since it's permitted to differ
|
|
||||||
* from peers obviously.
|
|
||||||
*/
|
*/
|
||||||
COM_RESERVED_ID_ISSUED_TO = 2
|
COM_RESERVED_ID_ISSUED_TO = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an empty certificate
|
* Create an empty certificate of membership
|
||||||
*/
|
*/
|
||||||
CertificateOfMembership() :
|
CertificateOfMembership()
|
||||||
_qualifierCount(0)
|
|
||||||
{
|
{
|
||||||
memset(_signature.data,0,_signature.size());
|
memset(this,0,sizeof(CertificateOfMembership));
|
||||||
}
|
}
|
||||||
|
|
||||||
CertificateOfMembership(const CertificateOfMembership &c)
|
CertificateOfMembership(const CertificateOfMembership &c)
|
||||||
@@ -142,16 +114,16 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Create from required fields common to all networks
|
* Create from required fields common to all networks
|
||||||
*
|
*
|
||||||
* @param revision Revision number of certificate
|
* @param timestamp Timestamp of certificate
|
||||||
* @param timestampMaxDelta Maximum variation between timestamps on this net
|
* @param timestampMaxDelta Maximum variation between timestamps on this net
|
||||||
* @param nwid Network ID
|
* @param nwid Network ID
|
||||||
* @param issuedTo Certificate recipient
|
* @param issuedTo Certificate recipient
|
||||||
*/
|
*/
|
||||||
CertificateOfMembership(uint64_t revision,uint64_t revisionMaxDelta,uint64_t nwid,const Address &issuedTo)
|
CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo)
|
||||||
{
|
{
|
||||||
_qualifiers[0].id = COM_RESERVED_ID_REVISION;
|
_qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP;
|
||||||
_qualifiers[0].value = revision;
|
_qualifiers[0].value = timestamp;
|
||||||
_qualifiers[0].maxDelta = revisionMaxDelta;
|
_qualifiers[0].maxDelta = timestampMaxDelta;
|
||||||
_qualifiers[1].id = COM_RESERVED_ID_NETWORK_ID;
|
_qualifiers[1].id = COM_RESERVED_ID_NETWORK_ID;
|
||||||
_qualifiers[1].value = nwid;
|
_qualifiers[1].value = nwid;
|
||||||
_qualifiers[1].maxDelta = 0;
|
_qualifiers[1].maxDelta = 0;
|
||||||
@@ -168,22 +140,6 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
|
||||||
/**
|
|
||||||
* Create from string-serialized data
|
|
||||||
*
|
|
||||||
* @param s String-serialized COM
|
|
||||||
*/
|
|
||||||
CertificateOfMembership(const char *s) { fromString(s); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create from string-serialized data
|
|
||||||
*
|
|
||||||
* @param s String-serialized COM
|
|
||||||
*/
|
|
||||||
CertificateOfMembership(const std::string &s) { fromString(s.c_str()); }
|
|
||||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create from binary-serialized COM in buffer
|
* Create from binary-serialized COM in buffer
|
||||||
*
|
*
|
||||||
@@ -202,45 +158,15 @@ public:
|
|||||||
inline operator bool() const throw() { return (_qualifierCount != 0); }
|
inline operator bool() const throw() { return (_qualifierCount != 0); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for presence of all required fields common to all networks
|
* @return Timestamp for this cert and maximum delta for timestamp
|
||||||
*
|
|
||||||
* @return True if all required fields are present
|
|
||||||
*/
|
*/
|
||||||
inline bool hasRequiredFields() const
|
inline std::pair<uint64_t,uint64_t> timestamp() const
|
||||||
{
|
|
||||||
if (_qualifierCount < 3)
|
|
||||||
return false;
|
|
||||||
if (_qualifiers[0].id != COM_RESERVED_ID_REVISION)
|
|
||||||
return false;
|
|
||||||
if (_qualifiers[1].id != COM_RESERVED_ID_NETWORK_ID)
|
|
||||||
return false;
|
|
||||||
if (_qualifiers[2].id != COM_RESERVED_ID_ISSUED_TO)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Maximum delta for mandatory revision field or 0 if field missing
|
|
||||||
*/
|
|
||||||
inline uint64_t revisionMaxDelta() const
|
|
||||||
{
|
{
|
||||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||||
if (_qualifiers[i].id == COM_RESERVED_ID_REVISION)
|
if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
|
||||||
return _qualifiers[i].maxDelta;
|
return std::pair<uint64_t,uint64_t>(_qualifiers[i].value,_qualifiers[i].maxDelta);
|
||||||
}
|
}
|
||||||
return 0ULL;
|
return std::pair<uint64_t,uint64_t>(0ULL,0ULL);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Revision number for this cert
|
|
||||||
*/
|
|
||||||
inline uint64_t revision() const
|
|
||||||
{
|
|
||||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
|
||||||
if (_qualifiers[i].id == COM_RESERVED_ID_REVISION)
|
|
||||||
return _qualifiers[i].value;
|
|
||||||
}
|
|
||||||
return 0ULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -321,12 +247,12 @@ public:
|
|||||||
bool sign(const Identity &with);
|
bool sign(const Identity &with);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify certificate against an identity
|
* Verify this COM and its signature
|
||||||
*
|
*
|
||||||
* @param id Identity to verify against
|
* @param RR Runtime environment for looking up peers
|
||||||
* @return True if certificate is signed by this identity and verification was successful
|
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
||||||
*/
|
*/
|
||||||
bool verify(const Identity &id) const;
|
int verify(const RuntimeEnvironment *RR) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if signed
|
* @return True if signed
|
||||||
@@ -341,7 +267,7 @@ public:
|
|||||||
template<unsigned int C>
|
template<unsigned int C>
|
||||||
inline void serialize(Buffer<C> &b) const
|
inline void serialize(Buffer<C> &b) const
|
||||||
{
|
{
|
||||||
b.append((unsigned char)COM_UINT64_ED25519);
|
b.append((uint8_t)1);
|
||||||
b.append((uint16_t)_qualifierCount);
|
b.append((uint16_t)_qualifierCount);
|
||||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||||
b.append(_qualifiers[i].id);
|
b.append(_qualifiers[i].id);
|
||||||
@@ -361,8 +287,8 @@ public:
|
|||||||
_qualifierCount = 0;
|
_qualifierCount = 0;
|
||||||
_signedBy.zero();
|
_signedBy.zero();
|
||||||
|
|
||||||
if (b[p++] != COM_UINT64_ED25519)
|
if (b[p++] != 1)
|
||||||
throw std::invalid_argument("invalid type");
|
throw std::invalid_argument("invalid object");
|
||||||
|
|
||||||
unsigned int numq = b.template at<uint16_t>(p); p += sizeof(uint16_t);
|
unsigned int numq = b.template at<uint16_t>(p); p += sizeof(uint16_t);
|
||||||
uint64_t lastId = 0;
|
uint64_t lastId = 0;
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
#include "Packet.hpp"
|
#include "Packet.hpp"
|
||||||
#include "Switch.hpp"
|
#include "Switch.hpp"
|
||||||
#include "Node.hpp"
|
#include "Node.hpp"
|
||||||
|
#include "Network.hpp"
|
||||||
#include "Array.hpp"
|
#include "Array.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
@@ -254,7 +255,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
|
|||||||
// One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
|
// One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
|
||||||
char polykey[ZT_POLY1305_KEY_LEN];
|
char polykey[ZT_POLY1305_KEY_LEN];
|
||||||
memset(polykey,0,sizeof(polykey));
|
memset(polykey,0,sizeof(polykey));
|
||||||
s20.encrypt12(polykey,polykey,sizeof(polykey));
|
s20.crypt12(polykey,polykey,sizeof(polykey));
|
||||||
|
|
||||||
// Compute 16-byte MAC
|
// Compute 16-byte MAC
|
||||||
char mac[ZT_POLY1305_MAC_LEN];
|
char mac[ZT_POLY1305_MAC_LEN];
|
||||||
@@ -266,7 +267,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
|
|||||||
|
|
||||||
// Decrypt!
|
// Decrypt!
|
||||||
dmsg.setSize(len - 24);
|
dmsg.setSize(len - 24);
|
||||||
s20.decrypt12(reinterpret_cast<const char *>(msg) + 24,const_cast<void *>(dmsg.data()),dmsg.size());
|
s20.crypt12(reinterpret_cast<const char *>(msg) + 24,const_cast<void *>(dmsg.data()),dmsg.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dmsg.size() < 4)
|
if (dmsg.size() < 4)
|
||||||
@@ -341,17 +342,20 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
|
|||||||
Identity id;
|
Identity id;
|
||||||
ptr += id.deserialize(dmsg,ptr);
|
ptr += id.deserialize(dmsg,ptr);
|
||||||
if (id) {
|
if (id) {
|
||||||
RR->topology->saveIdentity(id);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_remotePeers_m);
|
Mutex::Lock _l(_remotePeers_m);
|
||||||
_remotePeers[std::pair<Address,unsigned int>(id.address(),(unsigned int)fromMemberId)] = RR->node->now();
|
_RemotePeer &rp = _remotePeers[std::pair<Address,unsigned int>(id.address(),(unsigned int)fromMemberId)];
|
||||||
|
if (!rp.lastHavePeerReceived) {
|
||||||
|
RR->topology->saveIdentity(id);
|
||||||
|
RR->identity.agree(id,rp.key,ZT_PEER_SECRET_KEY_LENGTH);
|
||||||
|
}
|
||||||
|
rp.lastHavePeerReceived = RR->node->now();
|
||||||
}
|
}
|
||||||
|
|
||||||
_ClusterSendQueueEntry *q[16384]; // 16384 is "tons"
|
_ClusterSendQueueEntry *q[16384]; // 16384 is "tons"
|
||||||
unsigned int qc = _sendQueue->getByDest(id.address(),q,16384);
|
unsigned int qc = _sendQueue->getByDest(id.address(),q,16384);
|
||||||
for(unsigned int i=0;i<qc;++i)
|
for(unsigned int i=0;i<qc;++i)
|
||||||
this->sendViaCluster(q[i]->fromPeerAddress,q[i]->toPeerAddress,q[i]->data,q[i]->len,q[i]->unite);
|
this->relayViaCluster(q[i]->fromPeerAddress,q[i]->toPeerAddress,q[i]->data,q[i]->len,q[i]->unite);
|
||||||
_sendQueue->returnToPool(q,qc);
|
_sendQueue->returnToPool(q,qc);
|
||||||
|
|
||||||
TRACE("[%u] has %s (retried %u queued sends)",(unsigned int)fromMemberId,id.address().toString().c_str(),qc);
|
TRACE("[%u] has %s (retried %u queued sends)",(unsigned int)fromMemberId,id.address().toString().c_str(),qc);
|
||||||
@@ -361,7 +365,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
|
|||||||
case CLUSTER_MESSAGE_WANT_PEER: {
|
case CLUSTER_MESSAGE_WANT_PEER: {
|
||||||
const Address zeroTierAddress(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH;
|
const Address zeroTierAddress(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH;
|
||||||
SharedPtr<Peer> peer(RR->topology->getPeerNoCache(zeroTierAddress));
|
SharedPtr<Peer> peer(RR->topology->getPeerNoCache(zeroTierAddress));
|
||||||
if ( (peer) && (peer->hasClusterOptimalPath(RR->node->now())) ) {
|
if ( (peer) && (peer->hasLocalClusterOptimalPath(RR->node->now())) ) {
|
||||||
Buffer<1024> buf;
|
Buffer<1024> buf;
|
||||||
peer->identity().serialize(buf);
|
peer->identity().serialize(buf);
|
||||||
Mutex::Lock _l2(_members[fromMemberId].lock);
|
Mutex::Lock _l2(_members[fromMemberId].lock);
|
||||||
@@ -396,7 +400,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
|
|||||||
SharedPtr<Peer> localPeer(RR->topology->getPeerNoCache(localPeerAddress));
|
SharedPtr<Peer> localPeer(RR->topology->getPeerNoCache(localPeerAddress));
|
||||||
if ((localPeer)&&(numRemotePeerPaths > 0)) {
|
if ((localPeer)&&(numRemotePeerPaths > 0)) {
|
||||||
InetAddress bestLocalV4,bestLocalV6;
|
InetAddress bestLocalV4,bestLocalV6;
|
||||||
localPeer->getBestActiveAddresses(now,bestLocalV4,bestLocalV6);
|
localPeer->getRendezvousAddresses(now,bestLocalV4,bestLocalV6);
|
||||||
|
|
||||||
InetAddress bestRemoteV4,bestRemoteV6;
|
InetAddress bestRemoteV4,bestRemoteV6;
|
||||||
for(unsigned int i=0;i<numRemotePeerPaths;++i) {
|
for(unsigned int i=0;i<numRemotePeerPaths;++i) {
|
||||||
@@ -455,7 +459,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
|
|||||||
Mutex::Lock _l2(_members[fromMemberId].lock);
|
Mutex::Lock _l2(_members[fromMemberId].lock);
|
||||||
_send(fromMemberId,CLUSTER_MESSAGE_PROXY_SEND,rendezvousForRemote.data(),rendezvousForRemote.size());
|
_send(fromMemberId,CLUSTER_MESSAGE_PROXY_SEND,rendezvousForRemote.data(),rendezvousForRemote.size());
|
||||||
}
|
}
|
||||||
RR->sw->send(rendezvousForLocal,true,0);
|
RR->sw->send(rendezvousForLocal,true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -466,9 +470,18 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
|
|||||||
const unsigned int len = dmsg.at<uint16_t>(ptr); ptr += 2;
|
const unsigned int len = dmsg.at<uint16_t>(ptr); ptr += 2;
|
||||||
Packet outp(rcpt,RR->identity.address(),verb);
|
Packet outp(rcpt,RR->identity.address(),verb);
|
||||||
outp.append(dmsg.field(ptr,len),len); ptr += len;
|
outp.append(dmsg.field(ptr,len),len); ptr += len;
|
||||||
RR->sw->send(outp,true,0);
|
RR->sw->send(outp,true);
|
||||||
//TRACE("[%u] proxy send %s to %s length %u",(unsigned int)fromMemberId,Packet::verbString(verb),rcpt.toString().c_str(),len);
|
//TRACE("[%u] proxy send %s to %s length %u",(unsigned int)fromMemberId,Packet::verbString(verb),rcpt.toString().c_str(),len);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case CLUSTER_MESSAGE_NETWORK_CONFIG: {
|
||||||
|
const SharedPtr<Network> network(RR->node->network(dmsg.at<uint64_t>(ptr)));
|
||||||
|
if (network) {
|
||||||
|
// Copy into a Packet just to conform to Network API. Eventually
|
||||||
|
// will want to refactor.
|
||||||
|
network->handleConfigChunk(0,Address(),Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(dmsg),ptr);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
TRACE("invalid message of size %u type %d (inner decode), discarding",mlen,mtype);
|
TRACE("invalid message of size %u type %d (inner decode), discarding",mlen,mtype);
|
||||||
@@ -494,35 +507,51 @@ void Cluster::broadcastHavePeer(const Identity &id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite)
|
void Cluster::broadcastNetworkConfigChunk(const void *chunk,unsigned int len)
|
||||||
{
|
{
|
||||||
if (len > ZT_PROTO_MAX_PACKET_LENGTH) // sanity check
|
Mutex::Lock _l(_memberIds_m);
|
||||||
return;
|
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
|
||||||
|
Mutex::Lock _l2(_members[*mid].lock);
|
||||||
|
_send(*mid,CLUSTER_MESSAGE_NETWORK_CONFIG,chunk,len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Cluster::checkSendViaCluster(const Address &toPeerAddress,uint64_t &mostRecentTs,void *peerSecret)
|
||||||
|
{
|
||||||
const uint64_t now = RR->node->now();
|
const uint64_t now = RR->node->now();
|
||||||
|
mostRecentTs = 0;
|
||||||
uint64_t mostRecentTs = 0;
|
int mostRecentMemberId = -1;
|
||||||
unsigned int mostRecentMemberId = 0xffffffff;
|
|
||||||
{
|
{
|
||||||
Mutex::Lock _l2(_remotePeers_m);
|
Mutex::Lock _l2(_remotePeers_m);
|
||||||
std::map< std::pair<Address,unsigned int>,uint64_t >::const_iterator rpe(_remotePeers.lower_bound(std::pair<Address,unsigned int>(toPeerAddress,0)));
|
std::map< std::pair<Address,unsigned int>,_RemotePeer >::const_iterator rpe(_remotePeers.lower_bound(std::pair<Address,unsigned int>(toPeerAddress,0)));
|
||||||
for(;;) {
|
for(;;) {
|
||||||
if ((rpe == _remotePeers.end())||(rpe->first.first != toPeerAddress))
|
if ((rpe == _remotePeers.end())||(rpe->first.first != toPeerAddress))
|
||||||
break;
|
break;
|
||||||
else if (rpe->second > mostRecentTs) {
|
else if (rpe->second.lastHavePeerReceived > mostRecentTs) {
|
||||||
mostRecentTs = rpe->second;
|
mostRecentTs = rpe->second.lastHavePeerReceived;
|
||||||
mostRecentMemberId = rpe->first.second;
|
memcpy(peerSecret,rpe->second.key,ZT_PEER_SECRET_KEY_LENGTH);
|
||||||
|
mostRecentMemberId = (int)rpe->first.second;
|
||||||
}
|
}
|
||||||
++rpe;
|
++rpe;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint64_t age = now - mostRecentTs;
|
const uint64_t ageOfMostRecentHavePeerAnnouncement = now - mostRecentTs;
|
||||||
if (age >= (ZT_PEER_ACTIVITY_TIMEOUT / 3)) {
|
if (ageOfMostRecentHavePeerAnnouncement >= (ZT_PEER_ACTIVITY_TIMEOUT / 3)) {
|
||||||
const bool enqueueAndWait = ((age >= ZT_PEER_ACTIVITY_TIMEOUT)||(mostRecentMemberId > 0xffff));
|
if (ageOfMostRecentHavePeerAnnouncement >= ZT_PEER_ACTIVITY_TIMEOUT)
|
||||||
|
mostRecentMemberId = -1;
|
||||||
|
|
||||||
// Poll everyone with WANT_PEER if the age of our most recent entry is
|
bool sendWantPeer = true;
|
||||||
// approaching expiration (or has expired, or does not exist).
|
{
|
||||||
|
Mutex::Lock _l(_remotePeers_m);
|
||||||
|
_RemotePeer &rp = _remotePeers[std::pair<Address,unsigned int>(toPeerAddress,(unsigned int)_id)];
|
||||||
|
if ((now - rp.lastSentWantPeer) >= ZT_CLUSTER_WANT_PEER_EVERY) {
|
||||||
|
rp.lastSentWantPeer = now;
|
||||||
|
} else {
|
||||||
|
sendWantPeer = false; // don't flood WANT_PEER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sendWantPeer) {
|
||||||
char tmp[ZT_ADDRESS_LENGTH];
|
char tmp[ZT_ADDRESS_LENGTH];
|
||||||
toPeerAddress.copyTo(tmp,ZT_ADDRESS_LENGTH);
|
toPeerAddress.copyTo(tmp,ZT_ADDRESS_LENGTH);
|
||||||
{
|
{
|
||||||
@@ -532,23 +561,98 @@ void Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPee
|
|||||||
_send(*mid,CLUSTER_MESSAGE_WANT_PEER,tmp,ZT_ADDRESS_LENGTH);
|
_send(*mid,CLUSTER_MESSAGE_WANT_PEER,tmp,ZT_ADDRESS_LENGTH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mostRecentMemberId;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Cluster::sendViaCluster(int mostRecentMemberId,const Address &toPeerAddress,const void *data,unsigned int len)
|
||||||
|
{
|
||||||
|
if ((mostRecentMemberId < 0)||(mostRecentMemberId >= ZT_CLUSTER_MAX_MEMBERS)) // sanity check
|
||||||
|
return false;
|
||||||
|
Mutex::Lock _l2(_members[mostRecentMemberId].lock);
|
||||||
|
for(std::vector<InetAddress>::const_iterator i1(_zeroTierPhysicalEndpoints.begin());i1!=_zeroTierPhysicalEndpoints.end();++i1) {
|
||||||
|
for(std::vector<InetAddress>::const_iterator i2(_members[mostRecentMemberId].zeroTierPhysicalEndpoints.begin());i2!=_members[mostRecentMemberId].zeroTierPhysicalEndpoints.end();++i2) {
|
||||||
|
if (i1->ss_family == i2->ss_family) {
|
||||||
|
TRACE("sendViaCluster sending %u bytes to %s by way of %u (%s->%s)",len,toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId,i1->toString().c_str(),i2->toString().c_str());
|
||||||
|
RR->node->putPacket(*i1,*i2,data,len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cluster::relayViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite)
|
||||||
|
{
|
||||||
|
if (len > ZT_PROTO_MAX_PACKET_LENGTH) // sanity check
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint64_t now = RR->node->now();
|
||||||
|
|
||||||
|
uint64_t mostRecentTs = 0;
|
||||||
|
int mostRecentMemberId = -1;
|
||||||
|
{
|
||||||
|
Mutex::Lock _l2(_remotePeers_m);
|
||||||
|
std::map< std::pair<Address,unsigned int>,_RemotePeer >::const_iterator rpe(_remotePeers.lower_bound(std::pair<Address,unsigned int>(toPeerAddress,0)));
|
||||||
|
for(;;) {
|
||||||
|
if ((rpe == _remotePeers.end())||(rpe->first.first != toPeerAddress))
|
||||||
|
break;
|
||||||
|
else if (rpe->second.lastHavePeerReceived > mostRecentTs) {
|
||||||
|
mostRecentTs = rpe->second.lastHavePeerReceived;
|
||||||
|
mostRecentMemberId = (int)rpe->first.second;
|
||||||
|
}
|
||||||
|
++rpe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t ageOfMostRecentHavePeerAnnouncement = now - mostRecentTs;
|
||||||
|
if (ageOfMostRecentHavePeerAnnouncement >= (ZT_PEER_ACTIVITY_TIMEOUT / 3)) {
|
||||||
|
// Enqueue and wait if peer seems alive, but do WANT_PEER to refresh homing
|
||||||
|
const bool enqueueAndWait = ((ageOfMostRecentHavePeerAnnouncement >= ZT_PEER_ACTIVITY_TIMEOUT)||(mostRecentMemberId < 0));
|
||||||
|
|
||||||
|
// Poll everyone with WANT_PEER if the age of our most recent entry is
|
||||||
|
// approaching expiration (or has expired, or does not exist).
|
||||||
|
bool sendWantPeer = true;
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_remotePeers_m);
|
||||||
|
_RemotePeer &rp = _remotePeers[std::pair<Address,unsigned int>(toPeerAddress,(unsigned int)_id)];
|
||||||
|
if ((now - rp.lastSentWantPeer) >= ZT_CLUSTER_WANT_PEER_EVERY) {
|
||||||
|
rp.lastSentWantPeer = now;
|
||||||
|
} else {
|
||||||
|
sendWantPeer = false; // don't flood WANT_PEER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sendWantPeer) {
|
||||||
|
char tmp[ZT_ADDRESS_LENGTH];
|
||||||
|
toPeerAddress.copyTo(tmp,ZT_ADDRESS_LENGTH);
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_memberIds_m);
|
||||||
|
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
|
||||||
|
Mutex::Lock _l2(_members[*mid].lock);
|
||||||
|
_send(*mid,CLUSTER_MESSAGE_WANT_PEER,tmp,ZT_ADDRESS_LENGTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If there isn't a good place to send via, then enqueue this for retrying
|
// If there isn't a good place to send via, then enqueue this for retrying
|
||||||
// later and return after having broadcasted a WANT_PEER.
|
// later and return after having broadcasted a WANT_PEER.
|
||||||
if (enqueueAndWait) {
|
if (enqueueAndWait) {
|
||||||
TRACE("sendViaCluster %s -> %s enqueueing to wait for HAVE_PEER",fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str());
|
TRACE("relayViaCluster %s -> %s enqueueing to wait for HAVE_PEER",fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str());
|
||||||
_sendQueue->enqueue(now,fromPeerAddress,toPeerAddress,data,len,unite);
|
_sendQueue->enqueue(now,fromPeerAddress,toPeerAddress,data,len,unite);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mostRecentMemberId >= 0) {
|
||||||
Buffer<1024> buf;
|
Buffer<1024> buf;
|
||||||
if (unite) {
|
if (unite) {
|
||||||
InetAddress v4,v6;
|
InetAddress v4,v6;
|
||||||
if (fromPeerAddress) {
|
if (fromPeerAddress) {
|
||||||
SharedPtr<Peer> fromPeer(RR->topology->getPeerNoCache(fromPeerAddress));
|
SharedPtr<Peer> fromPeer(RR->topology->getPeerNoCache(fromPeerAddress));
|
||||||
if (fromPeer)
|
if (fromPeer)
|
||||||
fromPeer->getBestActiveAddresses(now,v4,v6);
|
fromPeer->getRendezvousAddresses(now,v4,v6);
|
||||||
}
|
}
|
||||||
uint8_t addrCount = 0;
|
uint8_t addrCount = 0;
|
||||||
if (v4)
|
if (v4)
|
||||||
@@ -574,15 +678,15 @@ void Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPee
|
|||||||
for(std::vector<InetAddress>::const_iterator i1(_zeroTierPhysicalEndpoints.begin());i1!=_zeroTierPhysicalEndpoints.end();++i1) {
|
for(std::vector<InetAddress>::const_iterator i1(_zeroTierPhysicalEndpoints.begin());i1!=_zeroTierPhysicalEndpoints.end();++i1) {
|
||||||
for(std::vector<InetAddress>::const_iterator i2(_members[mostRecentMemberId].zeroTierPhysicalEndpoints.begin());i2!=_members[mostRecentMemberId].zeroTierPhysicalEndpoints.end();++i2) {
|
for(std::vector<InetAddress>::const_iterator i2(_members[mostRecentMemberId].zeroTierPhysicalEndpoints.begin());i2!=_members[mostRecentMemberId].zeroTierPhysicalEndpoints.end();++i2) {
|
||||||
if (i1->ss_family == i2->ss_family) {
|
if (i1->ss_family == i2->ss_family) {
|
||||||
TRACE("sendViaCluster relaying %u bytes from %s to %s by way of %u (%s->%s)",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId,i1->toString().c_str(),i2->toString().c_str());
|
TRACE("relayViaCluster relaying %u bytes from %s to %s by way of %u (%s->%s)",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId,i1->toString().c_str(),i2->toString().c_str());
|
||||||
RR->node->putPacket(*i1,*i2,data,len);
|
RR->node->putPacket(*i1,*i2,data,len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("sendViaCluster relaying %u bytes from %s to %s by way of %u failed: no common endpoints with the same address family!",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId);
|
TRACE("relayViaCluster relaying %u bytes from %s to %s by way of %u failed: no common endpoints with the same address family!",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId);
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,8 +748,8 @@ void Cluster::doPeriodicTasks()
|
|||||||
_lastCleanedRemotePeers = now;
|
_lastCleanedRemotePeers = now;
|
||||||
|
|
||||||
Mutex::Lock _l(_remotePeers_m);
|
Mutex::Lock _l(_remotePeers_m);
|
||||||
for(std::map< std::pair<Address,unsigned int>,uint64_t >::iterator rp(_remotePeers.begin());rp!=_remotePeers.end();) {
|
for(std::map< std::pair<Address,unsigned int>,_RemotePeer >::iterator rp(_remotePeers.begin());rp!=_remotePeers.end();) {
|
||||||
if ((now - rp->second) >= ZT_PEER_ACTIVITY_TIMEOUT)
|
if ((now - rp->second.lastHavePeerReceived) >= ZT_PEER_ACTIVITY_TIMEOUT)
|
||||||
_remotePeers.erase(rp++);
|
_remotePeers.erase(rp++);
|
||||||
else ++rp;
|
else ++rp;
|
||||||
}
|
}
|
||||||
@@ -719,7 +823,9 @@ bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddr
|
|||||||
std::vector<InetAddress> best;
|
std::vector<InetAddress> best;
|
||||||
const double currentDistance = _dist3d(_x,_y,_z,px,py,pz);
|
const double currentDistance = _dist3d(_x,_y,_z,px,py,pz);
|
||||||
double bestDistance = (offload ? 2147483648.0 : currentDistance);
|
double bestDistance = (offload ? 2147483648.0 : currentDistance);
|
||||||
|
#ifdef ZT_TRACE
|
||||||
unsigned int bestMember = _id;
|
unsigned int bestMember = _id;
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_memberIds_m);
|
Mutex::Lock _l(_memberIds_m);
|
||||||
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
|
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
|
||||||
@@ -731,7 +837,9 @@ bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddr
|
|||||||
const double mdist = _dist3d(m.x,m.y,m.z,px,py,pz);
|
const double mdist = _dist3d(m.x,m.y,m.z,px,py,pz);
|
||||||
if (mdist < bestDistance) {
|
if (mdist < bestDistance) {
|
||||||
bestDistance = mdist;
|
bestDistance = mdist;
|
||||||
|
#ifdef ZT_TRACE
|
||||||
bestMember = *mid;
|
bestMember = *mid;
|
||||||
|
#endif
|
||||||
best = m.zeroTierPhysicalEndpoints;
|
best = m.zeroTierPhysicalEndpoints;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -754,6 +862,19 @@ bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Cluster::isClusterPeerFrontplane(const InetAddress &ip) const
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_memberIds_m);
|
||||||
|
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
|
||||||
|
Mutex::Lock _l2(_members[*mid].lock);
|
||||||
|
for(std::vector<InetAddress>::const_iterator i2(_members[*mid].zeroTierPhysicalEndpoints.begin());i2!=_members[*mid].zeroTierPhysicalEndpoints.end();++i2) {
|
||||||
|
if (ip == *i2)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void Cluster::status(ZT_ClusterStatus &status) const
|
void Cluster::status(ZT_ClusterStatus &status) const
|
||||||
{
|
{
|
||||||
const uint64_t now = RR->node->now();
|
const uint64_t now = RR->node->now();
|
||||||
@@ -833,10 +954,10 @@ void Cluster::_flush(uint16_t memberId)
|
|||||||
// One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
|
// One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
|
||||||
char polykey[ZT_POLY1305_KEY_LEN];
|
char polykey[ZT_POLY1305_KEY_LEN];
|
||||||
memset(polykey,0,sizeof(polykey));
|
memset(polykey,0,sizeof(polykey));
|
||||||
s20.encrypt12(polykey,polykey,sizeof(polykey));
|
s20.crypt12(polykey,polykey,sizeof(polykey));
|
||||||
|
|
||||||
// Encrypt m.q in place
|
// Encrypt m.q in place
|
||||||
s20.encrypt12(reinterpret_cast<const char *>(m.q.data()) + 24,const_cast<char *>(reinterpret_cast<const char *>(m.q.data())) + 24,m.q.size() - 24);
|
s20.crypt12(reinterpret_cast<const char *>(m.q.data()) + 24,const_cast<char *>(reinterpret_cast<const char *>(m.q.data())) + 24,m.q.size() - 24);
|
||||||
|
|
||||||
// Add MAC for authentication (encrypt-then-MAC)
|
// Add MAC for authentication (encrypt-then-MAC)
|
||||||
char mac[ZT_POLY1305_MAC_LEN];
|
char mac[ZT_POLY1305_MAC_LEN];
|
||||||
|
|||||||
@@ -88,6 +88,11 @@
|
|||||||
*/
|
*/
|
||||||
#define ZT_CLUSTER_SEND_QUEUE_DATA_MAX 1500
|
#define ZT_CLUSTER_SEND_QUEUE_DATA_MAX 1500
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We won't send WANT_PEER to other members more than every (ms) per recipient
|
||||||
|
*/
|
||||||
|
#define ZT_CLUSTER_WANT_PEER_EVERY 1000
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
class RuntimeEnvironment;
|
class RuntimeEnvironment;
|
||||||
@@ -216,14 +221,13 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Replicate a network config for a network we belong to:
|
* Replicate a network config for a network we belong to:
|
||||||
* <[8] 64-bit network ID>
|
* <[...] network config chunk>
|
||||||
* <[2] 16-bit length of network config>
|
|
||||||
* <[...] serialized network config>
|
|
||||||
*
|
*
|
||||||
* This is used by clusters to avoid every member having to query
|
* This is used by clusters to avoid every member having to query
|
||||||
* for the same netconf for networks all members belong to.
|
* for the same netconf for networks all members belong to.
|
||||||
*
|
*
|
||||||
* TODO: not implemented yet!
|
* The first field of a network config chunk is the network ID,
|
||||||
|
* so this can be checked to look up the network on receipt.
|
||||||
*/
|
*/
|
||||||
CLUSTER_MESSAGE_NETWORK_CONFIG = 7
|
CLUSTER_MESSAGE_NETWORK_CONFIG = 7
|
||||||
};
|
};
|
||||||
@@ -268,7 +272,38 @@ public:
|
|||||||
void broadcastHavePeer(const Identity &id);
|
void broadcastHavePeer(const Identity &id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send this packet via another node in this cluster if another node has this peer
|
* Broadcast a network config chunk to other members of cluster
|
||||||
|
*
|
||||||
|
* @param chunk Chunk data
|
||||||
|
* @param len Length of chunk
|
||||||
|
*/
|
||||||
|
void broadcastNetworkConfigChunk(const void *chunk,unsigned int len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the cluster has this peer, prepare the packet to send via cluster
|
||||||
|
*
|
||||||
|
* Note that outp is only armored (or modified at all) if the return value is a member ID.
|
||||||
|
*
|
||||||
|
* @param toPeerAddress Value of outp.destination(), simply to save additional lookup
|
||||||
|
* @param ts Result: set to time of last HAVE_PEER from the cluster
|
||||||
|
* @param peerSecret Result: Buffer to fill with peer secret on valid return value, must be at least ZT_PEER_SECRET_KEY_LENGTH bytes
|
||||||
|
* @return -1 if cluster does not know this peer, or a member ID to pass to sendViaCluster()
|
||||||
|
*/
|
||||||
|
int checkSendViaCluster(const Address &toPeerAddress,uint64_t &mostRecentTs,void *peerSecret);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send data via cluster front plane (packet head or fragment)
|
||||||
|
*
|
||||||
|
* @param haveMemberId Member ID that has this peer as returned by prepSendviaCluster()
|
||||||
|
* @param toPeerAddress Destination peer address
|
||||||
|
* @param data Packet or packet fragment data
|
||||||
|
* @param len Length of packet or fragment
|
||||||
|
* @return True if packet was sent (and outp was modified via armoring)
|
||||||
|
*/
|
||||||
|
bool sendViaCluster(int haveMemberId,const Address &toPeerAddress,const void *data,unsigned int len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relay a packet via the cluster
|
||||||
*
|
*
|
||||||
* This is used in the outgoing packet and relaying logic in Switch to
|
* This is used in the outgoing packet and relaying logic in Switch to
|
||||||
* relay packets to other cluster members. It isn't PROXY_SEND-- that is
|
* relay packets to other cluster members. It isn't PROXY_SEND-- that is
|
||||||
@@ -280,7 +315,7 @@ public:
|
|||||||
* @param len Length of packet or fragment
|
* @param len Length of packet or fragment
|
||||||
* @param unite If true, also request proxy unite across cluster
|
* @param unite If true, also request proxy unite across cluster
|
||||||
*/
|
*/
|
||||||
void sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite);
|
void relayViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a distributed query to other cluster members
|
* Send a distributed query to other cluster members
|
||||||
@@ -323,6 +358,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload);
|
bool findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ip Address to check
|
||||||
|
* @return True if this is a cluster frontplane address (excluding our addresses)
|
||||||
|
*/
|
||||||
|
bool isClusterPeerFrontplane(const InetAddress &ip) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill out ZT_ClusterStatus structure (from core API)
|
* Fill out ZT_ClusterStatus structure (from core API)
|
||||||
*
|
*
|
||||||
@@ -391,7 +432,15 @@ private:
|
|||||||
std::vector<uint16_t> _memberIds;
|
std::vector<uint16_t> _memberIds;
|
||||||
Mutex _memberIds_m;
|
Mutex _memberIds_m;
|
||||||
|
|
||||||
std::map< std::pair<Address,unsigned int>,uint64_t > _remotePeers; // we need ordered behavior and lower_bound here
|
struct _RemotePeer
|
||||||
|
{
|
||||||
|
_RemotePeer() : lastHavePeerReceived(0),lastSentWantPeer(0) {}
|
||||||
|
~_RemotePeer() { Utils::burn(key,ZT_PEER_SECRET_KEY_LENGTH); }
|
||||||
|
uint64_t lastHavePeerReceived;
|
||||||
|
uint64_t lastSentWantPeer;
|
||||||
|
uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]; // secret key from identity agreement
|
||||||
|
};
|
||||||
|
std::map< std::pair<Address,unsigned int>,_RemotePeer > _remotePeers; // we need ordered behavior and lower_bound here
|
||||||
Mutex _remotePeers_m;
|
Mutex _remotePeers_m;
|
||||||
|
|
||||||
uint64_t _lastFlushed;
|
uint64_t _lastFlushed;
|
||||||
|
|||||||
@@ -179,16 +179,16 @@
|
|||||||
*/
|
*/
|
||||||
#define ZT_PEER_SECRET_KEY_LENGTH 32
|
#define ZT_PEER_SECRET_KEY_LENGTH 32
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum delay between timer task checks to prevent thrashing
|
||||||
|
*/
|
||||||
|
#define ZT_CORE_TIMER_TASK_GRANULARITY 500
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How often Topology::clean() and Network::clean() and similar are called, in ms
|
* How often Topology::clean() and Network::clean() and similar are called, in ms
|
||||||
*/
|
*/
|
||||||
#define ZT_HOUSEKEEPING_PERIOD 120000
|
#define ZT_HOUSEKEEPING_PERIOD 120000
|
||||||
|
|
||||||
/**
|
|
||||||
* Overriding granularity for timer tasks to prevent CPU-intensive thrashing on every packet
|
|
||||||
*/
|
|
||||||
#define ZT_CORE_TIMER_TASK_GRANULARITY 500
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How long to remember peer records in RAM if they haven't been used
|
* How long to remember peer records in RAM if they haven't been used
|
||||||
*/
|
*/
|
||||||
@@ -214,6 +214,11 @@
|
|||||||
*/
|
*/
|
||||||
#define ZT_RECEIVE_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
|
#define ZT_RECEIVE_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum latency to allow for OK(HELLO) before packet is discarded
|
||||||
|
*/
|
||||||
|
#define ZT_HELLO_MAX_ALLOWABLE_LATENCY 60000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of ZT hops allowed (this is not IP hops/TTL)
|
* Maximum number of ZT hops allowed (this is not IP hops/TTL)
|
||||||
*
|
*
|
||||||
@@ -221,16 +226,31 @@
|
|||||||
*/
|
*/
|
||||||
#define ZT_RELAY_MAX_HOPS 3
|
#define ZT_RELAY_MAX_HOPS 3
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of upstreams to use (far more than we should ever need)
|
||||||
|
*/
|
||||||
|
#define ZT_MAX_UPSTREAMS 64
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expire time for multicast 'likes' and indirect multicast memberships in ms
|
* Expire time for multicast 'likes' and indirect multicast memberships in ms
|
||||||
*/
|
*/
|
||||||
#define ZT_MULTICAST_LIKE_EXPIRE 600000
|
#define ZT_MULTICAST_LIKE_EXPIRE 600000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Period for multicast LIKE announcements
|
||||||
|
*/
|
||||||
|
#define ZT_MULTICAST_ANNOUNCE_PERIOD 120000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delay between explicit MULTICAST_GATHER requests for a given multicast channel
|
* Delay between explicit MULTICAST_GATHER requests for a given multicast channel
|
||||||
*/
|
*/
|
||||||
#define ZT_MULTICAST_EXPLICIT_GATHER_DELAY (ZT_MULTICAST_LIKE_EXPIRE / 10)
|
#define ZT_MULTICAST_EXPLICIT_GATHER_DELAY (ZT_MULTICAST_LIKE_EXPIRE / 10)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expiration for credentials presented for MULTICAST_LIKE or MULTICAST_GATHER (for non-network-members)
|
||||||
|
*/
|
||||||
|
#define ZT_MULTICAST_CREDENTIAL_EXPIRATON ZT_MULTICAST_LIKE_EXPIRE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timeout for outgoing multicasts
|
* Timeout for outgoing multicasts
|
||||||
*
|
*
|
||||||
@@ -239,30 +259,49 @@
|
|||||||
#define ZT_MULTICAST_TRANSMIT_TIMEOUT 5000
|
#define ZT_MULTICAST_TRANSMIT_TIMEOUT 5000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default maximum number of peers to address with a single multicast (if unspecified in network config)
|
* Delay between checks of peer pings, etc., and also related housekeeping tasks
|
||||||
*/
|
*/
|
||||||
#define ZT_MULTICAST_DEFAULT_LIMIT 32
|
#define ZT_PING_CHECK_INVERVAL 5000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How frequently to send a zero-byte UDP keepalive packet
|
* How frequently to send heartbeats over in-use paths
|
||||||
*
|
|
||||||
* There are NATs with timeouts as short as 20 seconds, so this turns out
|
|
||||||
* to be needed.
|
|
||||||
*/
|
*/
|
||||||
#define ZT_NAT_KEEPALIVE_DELAY 19000
|
#define ZT_PATH_HEARTBEAT_PERIOD 14000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delay between scans of the topology active peer DB for peers that need ping
|
* Paths are considered inactive if they have not received traffic in this long
|
||||||
*
|
|
||||||
* This is also how often pings will be retried to upstream peers (relays, roots)
|
|
||||||
* constantly until something is heard.
|
|
||||||
*/
|
*/
|
||||||
#define ZT_PING_CHECK_INVERVAL 9500
|
#define ZT_PATH_ALIVE_TIMEOUT 45000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delay between ordinary case pings of direct links
|
* Minimum time between attempts to check dead paths to see if they can be re-awakened
|
||||||
*/
|
*/
|
||||||
#define ZT_PEER_DIRECT_PING_DELAY 60000
|
#define ZT_PATH_MIN_REACTIVATE_INTERVAL 2500
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not accept HELLOs over a given path more often than this
|
||||||
|
*/
|
||||||
|
#define ZT_PATH_HELLO_RATE_LIMIT 1000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delay between full-fledge pings of directly connected peers
|
||||||
|
*/
|
||||||
|
#define ZT_PEER_PING_PERIOD 60000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paths are considered expired if they have not produced a real packet in this long
|
||||||
|
*/
|
||||||
|
#define ZT_PEER_PATH_EXPIRATION ((ZT_PEER_PING_PERIOD * 4) + 3000)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a full HELLO every this often (ms)
|
||||||
|
*/
|
||||||
|
#define ZT_PEER_SEND_FULL_HELLO_EVERY (ZT_PEER_PING_PERIOD * 2)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How often to retry expired paths that we're still remembering
|
||||||
|
*/
|
||||||
|
#define ZT_PEER_EXPIRED_PATH_TRIAL_PERIOD (ZT_PEER_PING_PERIOD * 10)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timeout for overall peer activity (measured from last receive)
|
* Timeout for overall peer activity (measured from last receive)
|
||||||
@@ -270,19 +309,14 @@
|
|||||||
#define ZT_PEER_ACTIVITY_TIMEOUT 500000
|
#define ZT_PEER_ACTIVITY_TIMEOUT 500000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timeout for path activity
|
* General rate limit timeout for multiple packet types (HELLO, etc.)
|
||||||
*/
|
*/
|
||||||
#define ZT_PATH_ACTIVITY_TIMEOUT ZT_PEER_ACTIVITY_TIMEOUT
|
#define ZT_PEER_GENERAL_INBOUND_RATE_LIMIT 500
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* No answer timeout to trigger dead path detection
|
* General limit for max RTT for requests over the network
|
||||||
*/
|
*/
|
||||||
#define ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT 2000
|
#define ZT_GENERAL_RTT_LIMIT 5000
|
||||||
|
|
||||||
/**
|
|
||||||
* Probation threshold after which a path becomes dead
|
|
||||||
*/
|
|
||||||
#define ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION 3
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delay between requests for updated network autoconf information
|
* Delay between requests for updated network autoconf information
|
||||||
@@ -302,19 +336,9 @@
|
|||||||
#define ZT_MIN_UNITE_INTERVAL 30000
|
#define ZT_MIN_UNITE_INTERVAL 30000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delay between initial direct NAT-t packet and more aggressive techniques
|
* How often should peers try memorized or statically defined paths?
|
||||||
*
|
|
||||||
* This may also be a delay before sending the first packet if we determine
|
|
||||||
* that we should wait for the remote to initiate rendezvous first.
|
|
||||||
*/
|
*/
|
||||||
#define ZT_NAT_T_TACTICAL_ESCALATION_DELAY 1000
|
#define ZT_TRY_MEMORIZED_PATH_INTERVAL 30000
|
||||||
|
|
||||||
/**
|
|
||||||
* How long (max) to remember network certificates of membership?
|
|
||||||
*
|
|
||||||
* This only applies to networks we don't belong to.
|
|
||||||
*/
|
|
||||||
#define ZT_PEER_NETWORK_COM_EXPIRATION 3600000
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sanity limit on maximum bridge routes
|
* Sanity limit on maximum bridge routes
|
||||||
@@ -330,7 +354,7 @@
|
|||||||
/**
|
/**
|
||||||
* If there is no known route, spam to up to this many active bridges
|
* If there is no known route, spam to up to this many active bridges
|
||||||
*/
|
*/
|
||||||
#define ZT_MAX_BRIDGE_SPAM 16
|
#define ZT_MAX_BRIDGE_SPAM 32
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interval between direct path pushes in milliseconds
|
* Interval between direct path pushes in milliseconds
|
||||||
@@ -357,22 +381,49 @@
|
|||||||
#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 4
|
#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 4
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable support for old Dictionary based network configs
|
* Time horizon for VERB_NETWORK_CREDENTIALS cutoff
|
||||||
*/
|
*/
|
||||||
#define ZT_SUPPORT_OLD_STYLE_NETCONF 1
|
#define ZT_PEER_CREDENTIALS_CUTOFF_TIME 60000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A test pseudo-network-ID that can be joined
|
* Maximum number of VERB_NETWORK_CREDENTIALS within cutoff time
|
||||||
*
|
|
||||||
* Joining this network ID will result in a network with no IP addressing
|
|
||||||
* and default parameters. No network configuration master will be consulted
|
|
||||||
* and instead a static config will be used. This is used in built-in testnet
|
|
||||||
* scenarios and can also be used for external testing.
|
|
||||||
*
|
|
||||||
* This is an impossible real network ID since 0xff is a reserved address
|
|
||||||
* prefix.
|
|
||||||
*/
|
*/
|
||||||
#define ZT_TEST_NETWORK_ID 0xffffffffffffffffULL
|
#define ZT_PEER_CREDEITIALS_CUTOFF_LIMIT 15
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General rate limit for other kinds of rate-limited packets (HELLO, credential request, etc.) both inbound and outbound
|
||||||
|
*/
|
||||||
|
#define ZT_PEER_GENERAL_RATE_LIMIT 1000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't do expensive identity validation more often than this
|
||||||
|
*
|
||||||
|
* IPv4 and IPv6 address prefixes are hashed down to 14-bit (0-16383) integers
|
||||||
|
* using the first 24 bits for IPv4 or the first 48 bits for IPv6. These are
|
||||||
|
* then rate limited to one identity validation per this often milliseconds.
|
||||||
|
*/
|
||||||
|
#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64) || defined(_M_AMD64))
|
||||||
|
// AMD64 machines can do anywhere from one every 50ms to one every 10ms. This provides plenty of margin.
|
||||||
|
#define ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT 2000
|
||||||
|
#else
|
||||||
|
#if (defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(_X86_) || defined(__I86__))
|
||||||
|
// 32-bit Intel machines usually average about one every 100ms
|
||||||
|
#define ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT 5000
|
||||||
|
#else
|
||||||
|
// This provides a safe margin for ARM, MIPS, etc. that usually average one every 250-400ms
|
||||||
|
#define ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT 10000
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long is a path or peer considered to have a trust relationship with us (for e.g. relay policy) since last trusted established packet?
|
||||||
|
*/
|
||||||
|
#define ZT_TRUST_EXPIRATION 600000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable support for older network configurations from older (pre-1.1.6) controllers
|
||||||
|
*/
|
||||||
|
#define ZT_SUPPORT_OLD_STYLE_NETCONF 1
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Desired buffer size for UDP sockets (used in service and osdep but defined here)
|
* Desired buffer size for UDP sockets (used in service and osdep but defined here)
|
||||||
@@ -383,6 +434,11 @@
|
|||||||
#define ZT_UDP_DESIRED_BUF_SIZE 131072
|
#define ZT_UDP_DESIRED_BUF_SIZE 131072
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Desired / recommended min stack size for threads (used on some platforms to reset thread stack size)
|
||||||
|
*/
|
||||||
|
#define ZT_THREAD_MIN_STACK_SIZE 1048576
|
||||||
|
|
||||||
/* Ethernet frame types that might be relevant to us */
|
/* Ethernet frame types that might be relevant to us */
|
||||||
#define ZT_ETHERTYPE_IPV4 0x0800
|
#define ZT_ETHERTYPE_IPV4 0x0800
|
||||||
#define ZT_ETHERTYPE_ARP 0x0806
|
#define ZT_ETHERTYPE_ARP 0x0806
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Constants.hpp"
|
|
||||||
#include "DeferredPackets.hpp"
|
|
||||||
#include "IncomingPacket.hpp"
|
|
||||||
#include "RuntimeEnvironment.hpp"
|
|
||||||
#include "Node.hpp"
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
DeferredPackets::DeferredPackets(const RuntimeEnvironment *renv) :
|
|
||||||
RR(renv),
|
|
||||||
_waiting(0),
|
|
||||||
_die(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
DeferredPackets::~DeferredPackets()
|
|
||||||
{
|
|
||||||
_q_m.lock();
|
|
||||||
_die = true;
|
|
||||||
_q_m.unlock();
|
|
||||||
|
|
||||||
for(;;) {
|
|
||||||
_q_s.post();
|
|
||||||
|
|
||||||
_q_m.lock();
|
|
||||||
if (_waiting <= 0) {
|
|
||||||
_q_m.unlock();
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
_q_m.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DeferredPackets::enqueue(IncomingPacket *pkt)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_q_m);
|
|
||||||
if (_q.size() >= ZT_DEFFEREDPACKETS_MAX)
|
|
||||||
return false;
|
|
||||||
_q.push_back(*pkt);
|
|
||||||
}
|
|
||||||
_q_s.post();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DeferredPackets::process()
|
|
||||||
{
|
|
||||||
std::list<IncomingPacket> pkt;
|
|
||||||
|
|
||||||
_q_m.lock();
|
|
||||||
|
|
||||||
if (_die) {
|
|
||||||
_q_m.unlock();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (_q.empty()) {
|
|
||||||
++_waiting;
|
|
||||||
_q_m.unlock();
|
|
||||||
_q_s.wait();
|
|
||||||
_q_m.lock();
|
|
||||||
--_waiting;
|
|
||||||
if (_die) {
|
|
||||||
_q_m.unlock();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move item from _q list to a dummy list here to avoid copying packet
|
|
||||||
pkt.splice(pkt.end(),_q,_q.begin());
|
|
||||||
|
|
||||||
_q_m.unlock();
|
|
||||||
|
|
||||||
try {
|
|
||||||
pkt.front().tryDecode(RR,true);
|
|
||||||
} catch ( ... ) {} // drop invalids
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ZT_DEFERREDPACKETS_HPP
|
|
||||||
#define ZT_DEFERREDPACKETS_HPP
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
#include "Constants.hpp"
|
|
||||||
#include "SharedPtr.hpp"
|
|
||||||
#include "Mutex.hpp"
|
|
||||||
#include "DeferredPackets.hpp"
|
|
||||||
#include "BinarySemaphore.hpp"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of deferred packets
|
|
||||||
*/
|
|
||||||
#define ZT_DEFFEREDPACKETS_MAX 256
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
class IncomingPacket;
|
|
||||||
class RuntimeEnvironment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deferred packets
|
|
||||||
*
|
|
||||||
* IncomingPacket can defer its decoding this way by enqueueing itself here.
|
|
||||||
* When this is done, deferredDecode() is called later. This is done for
|
|
||||||
* operations that may be expensive to allow them to potentially be handled
|
|
||||||
* in the background or rate limited to maintain quality of service for more
|
|
||||||
* routine operations.
|
|
||||||
*/
|
|
||||||
class DeferredPackets
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DeferredPackets(const RuntimeEnvironment *renv);
|
|
||||||
~DeferredPackets();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enqueue a packet
|
|
||||||
*
|
|
||||||
* @param pkt Packet to process later (possibly in the background)
|
|
||||||
* @return False if queue is full
|
|
||||||
*/
|
|
||||||
bool enqueue(IncomingPacket *pkt);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for and then process a deferred packet
|
|
||||||
*
|
|
||||||
* If we are shutting down (in destructor), this returns -1 and should
|
|
||||||
* not be called again. Otherwise it returns the number of packets
|
|
||||||
* processed.
|
|
||||||
*
|
|
||||||
* @return Number processed or -1 if shutting down
|
|
||||||
*/
|
|
||||||
int process();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::list<IncomingPacket> _q;
|
|
||||||
const RuntimeEnvironment *const RR;
|
|
||||||
volatile int _waiting;
|
|
||||||
volatile bool _die;
|
|
||||||
Mutex _q_m;
|
|
||||||
BinarySemaphore _q_s;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -443,16 +443,14 @@ public:
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Dictionary data as a 0-terminated C-string
|
|
||||||
*/
|
|
||||||
inline const char *data() const { return _d; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Value of C template parameter
|
* @return Value of C template parameter
|
||||||
*/
|
*/
|
||||||
inline unsigned int capacity() const { return C; }
|
inline unsigned int capacity() const { return C; }
|
||||||
|
|
||||||
|
inline const char *data() const { return _d; }
|
||||||
|
inline char *unsafeData() { return _d; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char _d[C];
|
char _d[C];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -103,9 +103,9 @@ public:
|
|||||||
friend class Hashtable::Iterator;
|
friend class Hashtable::Iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param bc Initial capacity in buckets (default: 128, must be nonzero)
|
* @param bc Initial capacity in buckets (default: 64, must be nonzero)
|
||||||
*/
|
*/
|
||||||
Hashtable(unsigned long bc = 128) :
|
Hashtable(unsigned long bc = 64) :
|
||||||
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
|
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
|
||||||
_bc(bc),
|
_bc(bc),
|
||||||
_s(0)
|
_s(0)
|
||||||
@@ -362,7 +362,7 @@ private:
|
|||||||
template<typename O>
|
template<typename O>
|
||||||
static inline unsigned long _hc(const O &obj)
|
static inline unsigned long _hc(const O &obj)
|
||||||
{
|
{
|
||||||
return obj.hashCode();
|
return (unsigned long)obj.hashCode();
|
||||||
}
|
}
|
||||||
static inline unsigned long _hc(const uint64_t i)
|
static inline unsigned long _hc(const uint64_t i)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
|
|||||||
// but is not what we want for sequential memory-harndess.
|
// but is not what we want for sequential memory-harndess.
|
||||||
memset(genmem,0,ZT_IDENTITY_GEN_MEMORY);
|
memset(genmem,0,ZT_IDENTITY_GEN_MEMORY);
|
||||||
Salsa20 s20(digest,256,(char *)digest + 32);
|
Salsa20 s20(digest,256,(char *)digest + 32);
|
||||||
s20.encrypt20((char *)genmem,(char *)genmem,64);
|
s20.crypt20((char *)genmem,(char *)genmem,64);
|
||||||
for(unsigned long i=64;i<ZT_IDENTITY_GEN_MEMORY;i+=64) {
|
for(unsigned long i=64;i<ZT_IDENTITY_GEN_MEMORY;i+=64) {
|
||||||
unsigned long k = i - 64;
|
unsigned long k = i - 64;
|
||||||
*((uint64_t *)((char *)genmem + i)) = *((uint64_t *)((char *)genmem + k));
|
*((uint64_t *)((char *)genmem + i)) = *((uint64_t *)((char *)genmem + k));
|
||||||
@@ -57,7 +57,7 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
|
|||||||
*((uint64_t *)((char *)genmem + i + 40)) = *((uint64_t *)((char *)genmem + k + 40));
|
*((uint64_t *)((char *)genmem + i + 40)) = *((uint64_t *)((char *)genmem + k + 40));
|
||||||
*((uint64_t *)((char *)genmem + i + 48)) = *((uint64_t *)((char *)genmem + k + 48));
|
*((uint64_t *)((char *)genmem + i + 48)) = *((uint64_t *)((char *)genmem + k + 48));
|
||||||
*((uint64_t *)((char *)genmem + i + 56)) = *((uint64_t *)((char *)genmem + k + 56));
|
*((uint64_t *)((char *)genmem + i + 56)) = *((uint64_t *)((char *)genmem + k + 56));
|
||||||
s20.encrypt20((char *)genmem + i,(char *)genmem + i,64);
|
s20.crypt20((char *)genmem + i,(char *)genmem + i,64);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render final digest using genmem as a lookup table
|
// Render final digest using genmem as a lookup table
|
||||||
@@ -67,7 +67,7 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
|
|||||||
uint64_t tmp = ((uint64_t *)genmem)[idx2];
|
uint64_t tmp = ((uint64_t *)genmem)[idx2];
|
||||||
((uint64_t *)genmem)[idx2] = ((uint64_t *)digest)[idx1];
|
((uint64_t *)genmem)[idx2] = ((uint64_t *)digest)[idx1];
|
||||||
((uint64_t *)digest)[idx1] = tmp;
|
((uint64_t *)digest)[idx1] = tmp;
|
||||||
s20.encrypt20(digest,digest,64);
|
s20.crypt20(digest,digest,64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ std::string Identity::toString(bool includePrivate) const
|
|||||||
std::string r;
|
std::string r;
|
||||||
|
|
||||||
r.append(_address.toString());
|
r.append(_address.toString());
|
||||||
r.append(":0:"); // 0 == IDENTITY_TYPE_C25519
|
r.append(":0:"); // 0 == ZT_OBJECT_TYPE_IDENTITY
|
||||||
r.append(Utils::hex(_publicKey.data,(unsigned int)_publicKey.size()));
|
r.append(Utils::hex(_publicKey.data,(unsigned int)_publicKey.size()));
|
||||||
if ((_privateKey)&&(includePrivate)) {
|
if ((_privateKey)&&(includePrivate)) {
|
||||||
r.push_back(':');
|
r.push_back(':');
|
||||||
@@ -160,7 +160,7 @@ bool Identity::fromString(const char *str)
|
|||||||
for(char *f=Utils::stok(tmp,":",&saveptr);(f);f=Utils::stok((char *)0,":",&saveptr)) {
|
for(char *f=Utils::stok(tmp,":",&saveptr);(f);f=Utils::stok((char *)0,":",&saveptr)) {
|
||||||
switch(fno++) {
|
switch(fno++) {
|
||||||
case 0:
|
case 0:
|
||||||
_address = Address(f);
|
_address = Address(Utils::hexStrToU64(f));
|
||||||
if (_address.isReserved())
|
if (_address.isReserved())
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -46,14 +46,6 @@ namespace ZeroTier {
|
|||||||
class Identity
|
class Identity
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* Identity types
|
|
||||||
*/
|
|
||||||
enum Type
|
|
||||||
{
|
|
||||||
IDENTITY_TYPE_C25519 = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
Identity() :
|
Identity() :
|
||||||
_privateKey((C25519::Private *)0)
|
_privateKey((C25519::Private *)0)
|
||||||
{
|
{
|
||||||
@@ -205,11 +197,6 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Identity type
|
|
||||||
*/
|
|
||||||
inline Type type() const throw() { return IDENTITY_TYPE_C25519; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return This identity's address
|
* @return This identity's address
|
||||||
*/
|
*/
|
||||||
@@ -226,7 +213,7 @@ public:
|
|||||||
inline void serialize(Buffer<C> &b,bool includePrivate = false) const
|
inline void serialize(Buffer<C> &b,bool includePrivate = false) const
|
||||||
{
|
{
|
||||||
_address.appendTo(b);
|
_address.appendTo(b);
|
||||||
b.append((unsigned char)IDENTITY_TYPE_C25519);
|
b.append((uint8_t)0); // C25519/Ed25519 identity type
|
||||||
b.append(_publicKey.data,(unsigned int)_publicKey.size());
|
b.append(_publicKey.data,(unsigned int)_publicKey.size());
|
||||||
if ((_privateKey)&&(includePrivate)) {
|
if ((_privateKey)&&(includePrivate)) {
|
||||||
b.append((unsigned char)_privateKey->size());
|
b.append((unsigned char)_privateKey->size());
|
||||||
@@ -257,7 +244,7 @@ public:
|
|||||||
_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||||
p += ZT_ADDRESS_LENGTH;
|
p += ZT_ADDRESS_LENGTH;
|
||||||
|
|
||||||
if (b[p++] != IDENTITY_TYPE_C25519)
|
if (b[p++] != 0)
|
||||||
throw std::invalid_argument("unsupported identity type");
|
throw std::invalid_argument("unsupported identity type");
|
||||||
|
|
||||||
memcpy(_publicKey.data,b.field(p,(unsigned int)_publicKey.size()),(unsigned int)_publicKey.size());
|
memcpy(_publicKey.data,b.field(p,(unsigned int)_publicKey.size()),(unsigned int)_publicKey.size());
|
||||||
@@ -295,6 +282,24 @@ public:
|
|||||||
bool fromString(const char *str);
|
bool fromString(const char *str);
|
||||||
inline bool fromString(const std::string &str) { return fromString(str.c_str()); }
|
inline bool fromString(const std::string &str) { return fromString(str.c_str()); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return C25519 public key
|
||||||
|
*/
|
||||||
|
inline const C25519::Public &publicKey() const { return _publicKey; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return C25519 key pair (only returns valid pair if private key is present in this Identity object)
|
||||||
|
*/
|
||||||
|
inline const C25519::Pair privateKeyPair() const
|
||||||
|
{
|
||||||
|
C25519::Pair pair;
|
||||||
|
pair.pub = _publicKey;
|
||||||
|
if (_privateKey)
|
||||||
|
pair.priv = *_privateKey;
|
||||||
|
else memset(pair.priv.data,0,ZT_C25519_PRIVATE_KEY_LEN);
|
||||||
|
return pair;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if this identity contains something
|
* @return True if this identity contains something
|
||||||
*/
|
*/
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,7 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "Packet.hpp"
|
#include "Packet.hpp"
|
||||||
#include "InetAddress.hpp"
|
#include "Path.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "MulticastGroup.hpp"
|
#include "MulticastGroup.hpp"
|
||||||
#include "Peer.hpp"
|
#include "Peer.hpp"
|
||||||
@@ -56,59 +56,40 @@ class IncomingPacket : public Packet
|
|||||||
public:
|
public:
|
||||||
IncomingPacket() :
|
IncomingPacket() :
|
||||||
Packet(),
|
Packet(),
|
||||||
_receiveTime(0),
|
_receiveTime(0)
|
||||||
_localAddress(),
|
|
||||||
_remoteAddress()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
IncomingPacket(const IncomingPacket &p)
|
|
||||||
{
|
|
||||||
// All fields including InetAddress are memcpy'able
|
|
||||||
memcpy(this,&p,sizeof(IncomingPacket));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new packet-in-decode
|
* Create a new packet-in-decode
|
||||||
*
|
*
|
||||||
* @param data Packet data
|
* @param data Packet data
|
||||||
* @param len Packet length
|
* @param len Packet length
|
||||||
* @param localAddress Local interface address
|
* @param path Path over which packet arrived
|
||||||
* @param remoteAddress Address from which packet came
|
|
||||||
* @param now Current time
|
* @param now Current time
|
||||||
* @throws std::out_of_range Range error processing packet
|
* @throws std::out_of_range Range error processing packet
|
||||||
*/
|
*/
|
||||||
IncomingPacket(const void *data,unsigned int len,const InetAddress &localAddress,const InetAddress &remoteAddress,uint64_t now) :
|
IncomingPacket(const void *data,unsigned int len,const SharedPtr<Path> &path,uint64_t now) :
|
||||||
Packet(data,len),
|
Packet(data,len),
|
||||||
_receiveTime(now),
|
_receiveTime(now),
|
||||||
_localAddress(localAddress),
|
_path(path)
|
||||||
_remoteAddress(remoteAddress)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
inline IncomingPacket &operator=(const IncomingPacket &p)
|
|
||||||
{
|
|
||||||
// All fields including InetAddress are memcpy'able
|
|
||||||
memcpy(this,&p,sizeof(IncomingPacket));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init packet-in-decode in place
|
* Init packet-in-decode in place
|
||||||
*
|
*
|
||||||
* @param data Packet data
|
* @param data Packet data
|
||||||
* @param len Packet length
|
* @param len Packet length
|
||||||
* @param localAddress Local interface address
|
* @param path Path over which packet arrived
|
||||||
* @param remoteAddress Address from which packet came
|
|
||||||
* @param now Current time
|
* @param now Current time
|
||||||
* @throws std::out_of_range Range error processing packet
|
* @throws std::out_of_range Range error processing packet
|
||||||
*/
|
*/
|
||||||
inline void init(const void *data,unsigned int len,const InetAddress &localAddress,const InetAddress &remoteAddress,uint64_t now)
|
inline void init(const void *data,unsigned int len,const SharedPtr<Path> &path,uint64_t now)
|
||||||
{
|
{
|
||||||
copyFrom(data,len);
|
copyFrom(data,len);
|
||||||
_receiveTime = now;
|
_receiveTime = now;
|
||||||
_localAddress = localAddress;
|
_path = path;
|
||||||
_remoteAddress = remoteAddress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,53 +99,23 @@ public:
|
|||||||
* about whether the packet was valid. A rejection is 'complete.'
|
* about whether the packet was valid. A rejection is 'complete.'
|
||||||
*
|
*
|
||||||
* Once true is returned, this must not be called again. The packet's state
|
* Once true is returned, this must not be called again. The packet's state
|
||||||
* may no longer be valid. The only exception is deferred decoding. In this
|
* may no longer be valid.
|
||||||
* case true is returned to indicate to the normal decode path that it is
|
|
||||||
* finished with the packet. The packet will have added itself to the
|
|
||||||
* deferred queue and will expect tryDecode() to be called one more time
|
|
||||||
* with deferred set to true.
|
|
||||||
*
|
|
||||||
* Deferred decoding is performed by DeferredPackets.cpp and should not be
|
|
||||||
* done elsewhere. Under deferred decoding packets only get one shot and
|
|
||||||
* so the return value of tryDecode() is ignored.
|
|
||||||
*
|
*
|
||||||
* @param RR Runtime environment
|
* @param RR Runtime environment
|
||||||
* @param deferred If true, this is a deferred decode and the return is ignored
|
|
||||||
* @return True if decoding and processing is complete, false if caller should try again
|
* @return True if decoding and processing is complete, false if caller should try again
|
||||||
*/
|
*/
|
||||||
bool tryDecode(const RuntimeEnvironment *RR,bool deferred);
|
bool tryDecode(const RuntimeEnvironment *RR);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Time of packet receipt / start of decode
|
* @return Time of packet receipt / start of decode
|
||||||
*/
|
*/
|
||||||
inline uint64_t receiveTime() const throw() { return _receiveTime; }
|
inline uint64_t receiveTime() const throw() { return _receiveTime; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the Salsa20/12+SHA512 proof of work function
|
|
||||||
*
|
|
||||||
* @param difficulty Difficulty in bits (max: 64)
|
|
||||||
* @param challenge Challenge string
|
|
||||||
* @param challengeLength Length of challenge in bytes (max allowed: ZT_PROTO_MAX_PACKET_LENGTH)
|
|
||||||
* @param result Buffer to fill with 16-byte result
|
|
||||||
*/
|
|
||||||
static void computeSalsa2012Sha512ProofOfWork(unsigned int difficulty,const void *challenge,unsigned int challengeLength,unsigned char result[16]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify the result of Salsa20/12+SHA512 proof of work
|
|
||||||
*
|
|
||||||
* @param difficulty Difficulty in bits (max: 64)
|
|
||||||
* @param challenge Challenge bytes
|
|
||||||
* @param challengeLength Length of challenge in bytes (max allowed: ZT_PROTO_MAX_PACKET_LENGTH)
|
|
||||||
* @param proposedResult Result supplied by client
|
|
||||||
* @return True if result is valid
|
|
||||||
*/
|
|
||||||
static bool testSalsa2012Sha512ProofOfWorkResult(unsigned int difficulty,const void *challenge,unsigned int challengeLength,const unsigned char proposedResult[16]);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// These are called internally to handle packet contents once it has
|
// These are called internally to handle packet contents once it has
|
||||||
// been authenticated, decrypted, decompressed, and classified.
|
// been authenticated, decrypted, decompressed, and classified.
|
||||||
bool _doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer); // can be called with NULL peer, while all others cannot
|
bool _doHELLO(const RuntimeEnvironment *RR,const bool alreadyAuthenticated);
|
||||||
bool _doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
@@ -172,22 +123,20 @@ private:
|
|||||||
bool _doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doNETWORK_CONFIG(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doUSER_MESSAGE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
|
|
||||||
// Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to communicate
|
void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,const uint64_t nwid);
|
||||||
void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid);
|
|
||||||
|
|
||||||
uint64_t _receiveTime;
|
uint64_t _receiveTime;
|
||||||
InetAddress _localAddress;
|
SharedPtr<Path> _path;
|
||||||
InetAddress _remoteAddress;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ void InetAddress::set(const std::string &ip,unsigned int port)
|
|||||||
sin6->sin6_port = Utils::hton((uint16_t)port);
|
sin6->sin6_port = Utils::hton((uint16_t)port);
|
||||||
if (inet_pton(AF_INET6,ip.c_str(),(void *)&(sin6->sin6_addr.s6_addr)) <= 0)
|
if (inet_pton(AF_INET6,ip.c_str(),(void *)&(sin6->sin6_addr.s6_addr)) <= 0)
|
||||||
memset(this,0,sizeof(InetAddress));
|
memset(this,0,sizeof(InetAddress));
|
||||||
} else {
|
} else if (ip.find('.') != std::string::npos) {
|
||||||
struct sockaddr_in *sin = reinterpret_cast<struct sockaddr_in *>(this);
|
struct sockaddr_in *sin = reinterpret_cast<struct sockaddr_in *>(this);
|
||||||
ss_family = AF_INET;
|
ss_family = AF_INET;
|
||||||
sin->sin_port = Utils::hton((uint16_t)port);
|
sin->sin_port = Utils::hton((uint16_t)port);
|
||||||
@@ -236,8 +236,14 @@ InetAddress InetAddress::netmask() const
|
|||||||
case AF_INET6: {
|
case AF_INET6: {
|
||||||
uint64_t nm[2];
|
uint64_t nm[2];
|
||||||
const unsigned int bits = netmaskBits();
|
const unsigned int bits = netmaskBits();
|
||||||
|
if(bits) {
|
||||||
nm[0] = Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
|
nm[0] = Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
|
||||||
nm[1] = Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
|
nm[1] = Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nm[0] = 0;
|
||||||
|
nm[1] = 0;
|
||||||
|
}
|
||||||
memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
|
memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -300,6 +300,19 @@ struct InetAddress : public sockaddr_storage
|
|||||||
*/
|
*/
|
||||||
inline unsigned int netmaskBits() const throw() { return port(); }
|
inline unsigned int netmaskBits() const throw() { return port(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if netmask bits is valid for the address type
|
||||||
|
*/
|
||||||
|
inline bool netmaskBitsValid() const
|
||||||
|
{
|
||||||
|
const unsigned int n = port();
|
||||||
|
switch(ss_family) {
|
||||||
|
case AF_INET: return (n <= 32);
|
||||||
|
case AF_INET6: return (n <= 128);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alias for port()
|
* Alias for port()
|
||||||
*
|
*
|
||||||
@@ -356,7 +369,6 @@ struct InetAddress : public sockaddr_storage
|
|||||||
* @return pointer to raw address bytes or NULL if not available
|
* @return pointer to raw address bytes or NULL if not available
|
||||||
*/
|
*/
|
||||||
inline const void *rawIpData() const
|
inline const void *rawIpData() const
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
switch(ss_family) {
|
switch(ss_family) {
|
||||||
case AF_INET: return (const void *)&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
|
case AF_INET: return (const void *)&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
|
||||||
@@ -365,6 +377,25 @@ struct InetAddress : public sockaddr_storage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return InetAddress containing only the IP portion of this address and a zero port, or NULL if not IPv4 or IPv6
|
||||||
|
*/
|
||||||
|
inline InetAddress ipOnly() const
|
||||||
|
{
|
||||||
|
InetAddress r;
|
||||||
|
switch(ss_family) {
|
||||||
|
case AF_INET:
|
||||||
|
r.ss_family = AF_INET;
|
||||||
|
reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr = reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr;
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
r.ss_family = AF_INET6;
|
||||||
|
memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs an IP-only comparison or, if that is impossible, a memcmp()
|
* Performs an IP-only comparison or, if that is impossible, a memcmp()
|
||||||
*
|
*
|
||||||
@@ -383,6 +414,25 @@ struct InetAddress : public sockaddr_storage
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline unsigned long hashCode() const
|
||||||
|
{
|
||||||
|
if (ss_family == AF_INET) {
|
||||||
|
return ((unsigned long)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr + (unsigned long)reinterpret_cast<const struct sockaddr_in *>(this)->sin_port);
|
||||||
|
} else if (ss_family == AF_INET6) {
|
||||||
|
unsigned long tmp = reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port;
|
||||||
|
const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||||
|
for(long i=0;i<16;++i)
|
||||||
|
reinterpret_cast<uint8_t *>(&tmp)[i % sizeof(tmp)] ^= a[i];
|
||||||
|
return tmp;
|
||||||
|
} else {
|
||||||
|
unsigned long tmp = reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port;
|
||||||
|
const uint8_t *a = reinterpret_cast<const uint8_t *>(this);
|
||||||
|
for(long i=0;i<(long)sizeof(InetAddress);++i)
|
||||||
|
reinterpret_cast<uint8_t *>(&tmp)[i % sizeof(tmp)] ^= a[i];
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to null/zero
|
* Set to null/zero
|
||||||
*/
|
*/
|
||||||
@@ -399,6 +449,30 @@ struct InetAddress : public sockaddr_storage
|
|||||||
bool isNetwork() const
|
bool isNetwork() const
|
||||||
throw();
|
throw();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 14-bit (0-16383) hash of this IP's first 24 or 48 bits (for V4 or V6) for rate limiting code, or 0 if non-IP
|
||||||
|
*/
|
||||||
|
inline unsigned long rateGateHash() const
|
||||||
|
{
|
||||||
|
unsigned long h = 0;
|
||||||
|
switch(ss_family) {
|
||||||
|
case AF_INET:
|
||||||
|
h = (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr) & 0xffffff00) >> 8;
|
||||||
|
h ^= (h >> 14);
|
||||||
|
break;
|
||||||
|
case AF_INET6: {
|
||||||
|
const uint8_t *ip = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||||
|
h = ((unsigned long)ip[0]); h <<= 1;
|
||||||
|
h += ((unsigned long)ip[1]); h <<= 1;
|
||||||
|
h += ((unsigned long)ip[2]); h <<= 1;
|
||||||
|
h += ((unsigned long)ip[3]); h <<= 1;
|
||||||
|
h += ((unsigned long)ip[4]); h <<= 1;
|
||||||
|
h += ((unsigned long)ip[5]);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
return (h & 0x3fff);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if address family is non-zero
|
* @return True if address family is non-zero
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -60,16 +60,6 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
MulticastGroup(const char *s)
|
|
||||||
{
|
|
||||||
fromString(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
MulticastGroup(const std::string &s)
|
|
||||||
{
|
|
||||||
fromString(s.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derive the multicast group used for address resolution (ARP/NDP) for an IP
|
* Derive the multicast group used for address resolution (ARP/NDP) for an IP
|
||||||
*
|
*
|
||||||
@@ -106,22 +96,6 @@ public:
|
|||||||
return std::string(buf);
|
return std::string(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a human-readable multicast group
|
|
||||||
*
|
|
||||||
* @param s Multicast group in hex MAC/ADI format
|
|
||||||
*/
|
|
||||||
inline void fromString(const char *s)
|
|
||||||
{
|
|
||||||
char hex[17];
|
|
||||||
unsigned int hexlen = 0;
|
|
||||||
while ((*s)&&(*s != '/')&&(hexlen < (sizeof(hex) - 1)))
|
|
||||||
hex[hexlen++] = *s;
|
|
||||||
hex[hexlen] = (char)0;
|
|
||||||
_mac.fromString(hex);
|
|
||||||
_adi = (*s == '/') ? (uint32_t)Utils::hexStrToULong(s + 1) : (uint32_t)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Multicast address
|
* @return Multicast address
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ namespace ZeroTier {
|
|||||||
|
|
||||||
Multicaster::Multicaster(const RuntimeEnvironment *renv) :
|
Multicaster::Multicaster(const RuntimeEnvironment *renv) :
|
||||||
RR(renv),
|
RR(renv),
|
||||||
_groups(1024),
|
_groups(256),
|
||||||
_groups_m()
|
_gatherAuth(256)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,10 +152,10 @@ std::vector<Address> Multicaster::getMembers(uint64_t nwid,const MulticastGroup
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Multicaster::send(
|
void Multicaster::send(
|
||||||
const CertificateOfMembership *com,
|
|
||||||
unsigned int limit,
|
unsigned int limit,
|
||||||
uint64_t now,
|
uint64_t now,
|
||||||
uint64_t nwid,
|
uint64_t nwid,
|
||||||
|
bool disableCompression,
|
||||||
const std::vector<Address> &alwaysSendTo,
|
const std::vector<Address> &alwaysSendTo,
|
||||||
const MulticastGroup &mg,
|
const MulticastGroup &mg,
|
||||||
const MAC &src,
|
const MAC &src,
|
||||||
@@ -194,7 +194,7 @@ void Multicaster::send(
|
|||||||
RR,
|
RR,
|
||||||
now,
|
now,
|
||||||
nwid,
|
nwid,
|
||||||
com,
|
disableCompression,
|
||||||
limit,
|
limit,
|
||||||
1, // we'll still gather a little from peers to keep multicast list fresh
|
1, // we'll still gather a little from peers to keep multicast list fresh
|
||||||
src,
|
src,
|
||||||
@@ -226,35 +226,38 @@ void Multicaster::send(
|
|||||||
|
|
||||||
if ((gs.members.empty())||((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY)) {
|
if ((gs.members.empty())||((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY)) {
|
||||||
gs.lastExplicitGather = now;
|
gs.lastExplicitGather = now;
|
||||||
SharedPtr<Peer> explicitGatherPeers[2];
|
|
||||||
explicitGatherPeers[0] = RR->topology->getBestRoot();
|
|
||||||
const Address nwidc(Network::controllerFor(nwid));
|
|
||||||
if (nwidc != RR->identity.address())
|
|
||||||
explicitGatherPeers[1] = RR->topology->getPeer(nwidc);
|
|
||||||
for(unsigned int k=0;k<2;++k) {
|
|
||||||
const SharedPtr<Peer> &p = explicitGatherPeers[k];
|
|
||||||
if (!p)
|
|
||||||
continue;
|
|
||||||
//TRACE(">>MC upstream GATHER up to %u for group %.16llx/%s",gatherLimit,nwid,mg.toString().c_str());
|
|
||||||
|
|
||||||
const CertificateOfMembership *com = (CertificateOfMembership *)0;
|
Address explicitGatherPeers[16];
|
||||||
{
|
unsigned int numExplicitGatherPeers = 0;
|
||||||
SharedPtr<Network> nw(RR->node->network(nwid));
|
SharedPtr<Peer> bestRoot(RR->topology->getUpstreamPeer());
|
||||||
if ((nw)&&(nw->hasConfig())&&(nw->config().com)&&(nw->config().isPrivate())&&(p->needsOurNetworkMembershipCertificate(nwid,now,true)))
|
if (bestRoot)
|
||||||
com = &(nw->config().com);
|
explicitGatherPeers[numExplicitGatherPeers++] = bestRoot->address();
|
||||||
|
explicitGatherPeers[numExplicitGatherPeers++] = Network::controllerFor(nwid);
|
||||||
|
SharedPtr<Network> network(RR->node->network(nwid));
|
||||||
|
if (network) {
|
||||||
|
std::vector<Address> anchors(network->config().anchors());
|
||||||
|
for(std::vector<Address>::const_iterator a(anchors.begin());a!=anchors.end();++a) {
|
||||||
|
if (*a != RR->identity.address()) {
|
||||||
|
explicitGatherPeers[numExplicitGatherPeers++] = *a;
|
||||||
|
if (numExplicitGatherPeers == 16)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
|
for(unsigned int k=0;k<numExplicitGatherPeers;++k) {
|
||||||
|
const CertificateOfMembership *com = (network) ? ((network->config().com) ? &(network->config().com) : (const CertificateOfMembership *)0) : (const CertificateOfMembership *)0;
|
||||||
|
Packet outp(explicitGatherPeers[k],RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
|
||||||
outp.append(nwid);
|
outp.append(nwid);
|
||||||
outp.append((uint8_t)(com ? 0x01 : 0x00));
|
outp.append((uint8_t)((com) ? 0x01 : 0x00));
|
||||||
mg.mac().appendTo(outp);
|
mg.mac().appendTo(outp);
|
||||||
outp.append((uint32_t)mg.adi());
|
outp.append((uint32_t)mg.adi());
|
||||||
outp.append((uint32_t)gatherLimit);
|
outp.append((uint32_t)gatherLimit);
|
||||||
if (com)
|
if (com)
|
||||||
com->serialize(outp);
|
com->serialize(outp);
|
||||||
RR->sw->send(outp,true,0);
|
RR->node->expectReplyTo(outp.packetId());
|
||||||
|
RR->sw->send(outp,true);
|
||||||
}
|
}
|
||||||
gatherLimit = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gs.txQueue.push_back(OutboundMulticast());
|
gs.txQueue.push_back(OutboundMulticast());
|
||||||
@@ -264,7 +267,7 @@ void Multicaster::send(
|
|||||||
RR,
|
RR,
|
||||||
now,
|
now,
|
||||||
nwid,
|
nwid,
|
||||||
com,
|
disableCompression,
|
||||||
limit,
|
limit,
|
||||||
gatherLimit,
|
gatherLimit,
|
||||||
src,
|
src,
|
||||||
@@ -300,9 +303,9 @@ void Multicaster::send(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Multicaster::clean(uint64_t now)
|
void Multicaster::clean(uint64_t now)
|
||||||
|
{
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_groups_m);
|
Mutex::Lock _l(_groups_m);
|
||||||
|
|
||||||
Multicaster::Key *k = (Multicaster::Key *)0;
|
Multicaster::Key *k = (Multicaster::Key *)0;
|
||||||
MulticastGroupStatus *s = (MulticastGroupStatus *)0;
|
MulticastGroupStatus *s = (MulticastGroupStatus *)0;
|
||||||
Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
|
Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
|
||||||
@@ -337,6 +340,26 @@ void Multicaster::clean(uint64_t now)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_gatherAuth_m);
|
||||||
|
_GatherAuthKey *k = (_GatherAuthKey *)0;
|
||||||
|
uint64_t *ts = NULL;
|
||||||
|
Hashtable<_GatherAuthKey,uint64_t>::Iterator i(_gatherAuth);
|
||||||
|
while (i.next(k,ts)) {
|
||||||
|
if ((now - *ts) >= ZT_MULTICAST_CREDENTIAL_EXPIRATON)
|
||||||
|
_gatherAuth.erase(*k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Multicaster::addCredential(const CertificateOfMembership &com,bool alreadyValidated)
|
||||||
|
{
|
||||||
|
if ((alreadyValidated)||(com.verify(RR) == 0)) {
|
||||||
|
Mutex::Lock _l(_gatherAuth_m);
|
||||||
|
_gatherAuth[_GatherAuthKey(com.networkId(),com.issuedTo())] = RR->node->now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
|
void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
|
||||||
{
|
{
|
||||||
// assumes _groups_m is locked
|
// assumes _groups_m is locked
|
||||||
|
|||||||
@@ -150,10 +150,10 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Send a multicast
|
* Send a multicast
|
||||||
*
|
*
|
||||||
* @param com Certificate of membership to include or NULL for none
|
|
||||||
* @param limit Multicast limit
|
* @param limit Multicast limit
|
||||||
* @param now Current time
|
* @param now Current time
|
||||||
* @param nwid Network ID
|
* @param nwid Network ID
|
||||||
|
* @param disableCompression Disable packet payload compression?
|
||||||
* @param alwaysSendTo Send to these peers first and even if not included in subscriber list
|
* @param alwaysSendTo Send to these peers first and even if not included in subscriber list
|
||||||
* @param mg Multicast group
|
* @param mg Multicast group
|
||||||
* @param src Source Ethernet MAC address or NULL to skip in packet and compute from ZT address (non-bridged mode)
|
* @param src Source Ethernet MAC address or NULL to skip in packet and compute from ZT address (non-bridged mode)
|
||||||
@@ -162,10 +162,10 @@ public:
|
|||||||
* @param len Length of packet data
|
* @param len Length of packet data
|
||||||
*/
|
*/
|
||||||
void send(
|
void send(
|
||||||
const CertificateOfMembership *com,
|
|
||||||
unsigned int limit,
|
unsigned int limit,
|
||||||
uint64_t now,
|
uint64_t now,
|
||||||
uint64_t nwid,
|
uint64_t nwid,
|
||||||
|
bool disableCompression,
|
||||||
const std::vector<Address> &alwaysSendTo,
|
const std::vector<Address> &alwaysSendTo,
|
||||||
const MulticastGroup &mg,
|
const MulticastGroup &mg,
|
||||||
const MAC &src,
|
const MAC &src,
|
||||||
@@ -181,12 +181,52 @@ public:
|
|||||||
*/
|
*/
|
||||||
void clean(uint64_t now);
|
void clean(uint64_t now);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an authorization credential
|
||||||
|
*
|
||||||
|
* The Multicaster keeps its own track of when valid credentials of network
|
||||||
|
* membership are presented. This allows it to control MULTICAST_LIKE
|
||||||
|
* GATHER authorization for networks this node does not belong to.
|
||||||
|
*
|
||||||
|
* @param com Certificate of membership
|
||||||
|
* @param alreadyValidated If true, COM has already been checked and found to be valid and signed
|
||||||
|
*/
|
||||||
|
void addCredential(const CertificateOfMembership &com,bool alreadyValidated);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check authorization for GATHER and LIKE for non-network-members
|
||||||
|
*
|
||||||
|
* @param a Address of peer
|
||||||
|
* @param nwid Network ID
|
||||||
|
* @param now Current time
|
||||||
|
* @return True if GATHER and LIKE should be allowed
|
||||||
|
*/
|
||||||
|
bool cacheAuthorized(const Address &a,const uint64_t nwid,const uint64_t now) const
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_gatherAuth_m);
|
||||||
|
const uint64_t *p = _gatherAuth.get(_GatherAuthKey(nwid,a));
|
||||||
|
return ((p)&&((now - *p) < ZT_MULTICAST_CREDENTIAL_EXPIRATON));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
|
void _add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
|
||||||
|
|
||||||
const RuntimeEnvironment *RR;
|
const RuntimeEnvironment *RR;
|
||||||
|
|
||||||
Hashtable<Multicaster::Key,MulticastGroupStatus> _groups;
|
Hashtable<Multicaster::Key,MulticastGroupStatus> _groups;
|
||||||
Mutex _groups_m;
|
Mutex _groups_m;
|
||||||
|
|
||||||
|
struct _GatherAuthKey
|
||||||
|
{
|
||||||
|
_GatherAuthKey() : member(0),networkId(0) {}
|
||||||
|
_GatherAuthKey(const uint64_t nwid,const Address &a) : member(a.toInt()),networkId(nwid) {}
|
||||||
|
inline unsigned long hashCode() const { return (unsigned long)(member ^ networkId); }
|
||||||
|
inline bool operator==(const _GatherAuthKey &k) const { return ((member == k.member)&&(networkId == k.networkId)); }
|
||||||
|
uint64_t member;
|
||||||
|
uint64_t networkId;
|
||||||
|
};
|
||||||
|
Hashtable< _GatherAuthKey,uint64_t > _gatherAuth;
|
||||||
|
Mutex _gatherAuth_m;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -40,14 +40,17 @@
|
|||||||
#include "MAC.hpp"
|
#include "MAC.hpp"
|
||||||
#include "Dictionary.hpp"
|
#include "Dictionary.hpp"
|
||||||
#include "Multicaster.hpp"
|
#include "Multicaster.hpp"
|
||||||
|
#include "Membership.hpp"
|
||||||
#include "NetworkConfig.hpp"
|
#include "NetworkConfig.hpp"
|
||||||
#include "CertificateOfMembership.hpp"
|
#include "CertificateOfMembership.hpp"
|
||||||
|
|
||||||
|
#define ZT_NETWORK_MAX_INCOMING_UPDATES 3
|
||||||
|
#define ZT_NETWORK_MAX_UPDATE_CHUNKS ((ZT_NETWORKCONFIG_DICT_CAPACITY / 1024) + 1)
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
class RuntimeEnvironment;
|
class RuntimeEnvironment;
|
||||||
class Peer;
|
class Peer;
|
||||||
class _MulticastAnnounceAll;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A virtual LAN
|
* A virtual LAN
|
||||||
@@ -55,7 +58,6 @@ class _MulticastAnnounceAll;
|
|||||||
class Network : NonCopyable
|
class Network : NonCopyable
|
||||||
{
|
{
|
||||||
friend class SharedPtr<Network>;
|
friend class SharedPtr<Network>;
|
||||||
friend class _MulticastAnnounceAll; // internal function object
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@@ -63,6 +65,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
static const MulticastGroup BROADCAST;
|
static const MulticastGroup BROADCAST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute primary controller device ID from network ID
|
||||||
|
*/
|
||||||
|
static inline Address controllerFor(uint64_t nwid) throw() { return Address(nwid >> 24); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new network
|
* Construct a new network
|
||||||
*
|
*
|
||||||
@@ -77,43 +84,78 @@ public:
|
|||||||
|
|
||||||
~Network();
|
~Network();
|
||||||
|
|
||||||
/**
|
inline uint64_t id() const { return _id; }
|
||||||
* @return Network ID
|
inline Address controller() const { return Address(_id >> 24); }
|
||||||
*/
|
inline bool multicastEnabled() const { return (_config.multicastLimit > 0); }
|
||||||
inline uint64_t id() const throw() { return _id; }
|
inline bool hasConfig() const { return (_config); }
|
||||||
|
inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
|
||||||
|
inline ZT_VirtualNetworkStatus status() const { Mutex::Lock _l(_lock); return _status(); }
|
||||||
|
inline const NetworkConfig &config() const { return _config; }
|
||||||
|
inline const MAC &mac() const { return _mac; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Address of network's controller (most significant 40 bits of ID)
|
* Apply filters to an outgoing packet
|
||||||
|
*
|
||||||
|
* This applies filters from our network config and, if that doesn't match,
|
||||||
|
* our capabilities in ascending order of capability ID. Additional actions
|
||||||
|
* such as TEE may be taken, and credentials may be pushed, so this is not
|
||||||
|
* side-effect-free. It's basically step one in sending something over VL2.
|
||||||
|
*
|
||||||
|
* @param noTee If true, do not TEE anything anywhere (for two-pass filtering as done with multicast and bridging)
|
||||||
|
* @param ztSource Source ZeroTier address
|
||||||
|
* @param ztDest Destination ZeroTier address
|
||||||
|
* @param macSource Ethernet layer source address
|
||||||
|
* @param macDest Ethernet layer destination address
|
||||||
|
* @param frameData Ethernet frame data
|
||||||
|
* @param frameLen Ethernet frame payload length
|
||||||
|
* @param etherType 16-bit ethernet type ID
|
||||||
|
* @param vlanId 16-bit VLAN ID
|
||||||
|
* @return True if packet should be sent, false if dropped or redirected
|
||||||
*/
|
*/
|
||||||
inline Address controller() const throw() { return Address(_id >> 24); }
|
bool filterOutgoingPacket(
|
||||||
|
const bool noTee,
|
||||||
|
const Address &ztSource,
|
||||||
|
const Address &ztDest,
|
||||||
|
const MAC &macSource,
|
||||||
|
const MAC &macDest,
|
||||||
|
const uint8_t *frameData,
|
||||||
|
const unsigned int frameLen,
|
||||||
|
const unsigned int etherType,
|
||||||
|
const unsigned int vlanId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param nwid Network ID
|
* Apply filters to an incoming packet
|
||||||
* @return Address of network's controller
|
*
|
||||||
|
* This applies filters from our network config and, if that doesn't match,
|
||||||
|
* the peer's capabilities in ascending order of capability ID. If there is
|
||||||
|
* a match certain actions may be taken such as sending a copy of the packet
|
||||||
|
* to a TEE or REDIRECT target.
|
||||||
|
*
|
||||||
|
* @param sourcePeer Source Peer
|
||||||
|
* @param ztDest Destination ZeroTier address
|
||||||
|
* @param macSource Ethernet layer source address
|
||||||
|
* @param macDest Ethernet layer destination address
|
||||||
|
* @param frameData Ethernet frame data
|
||||||
|
* @param frameLen Ethernet frame payload length
|
||||||
|
* @param etherType 16-bit ethernet type ID
|
||||||
|
* @param vlanId 16-bit VLAN ID
|
||||||
|
* @return 0 == drop, 1 == accept, 2 == accept even if bridged
|
||||||
*/
|
*/
|
||||||
static inline Address controllerFor(uint64_t nwid) throw() { return Address(nwid >> 24); }
|
int filterIncomingPacket(
|
||||||
|
const SharedPtr<Peer> &sourcePeer,
|
||||||
/**
|
const Address &ztDest,
|
||||||
* @return Multicast group memberships for this network's port (local, not learned via bridging)
|
const MAC &macSource,
|
||||||
*/
|
const MAC &macDest,
|
||||||
inline std::vector<MulticastGroup> multicastGroups() const
|
const uint8_t *frameData,
|
||||||
{
|
const unsigned int frameLen,
|
||||||
Mutex::Lock _l(_lock);
|
const unsigned int etherType,
|
||||||
return _myMulticastGroups;
|
const unsigned int vlanId);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return All multicast groups including learned groups that are behind any bridges we're attached to
|
|
||||||
*/
|
|
||||||
inline std::vector<MulticastGroup> allMulticastGroups() const
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
return _allMulticastGroups();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Check whether we are subscribed to a multicast group
|
||||||
|
*
|
||||||
* @param mg Multicast group
|
* @param mg Multicast group
|
||||||
* @param includeBridgedGroups If true, also include any groups we've learned via bridging
|
* @param includeBridgedGroups If true, also check groups we've learned via bridging
|
||||||
* @return True if this network endpoint / peer is a member
|
* @return True if this network endpoint / peer is a member
|
||||||
*/
|
*/
|
||||||
bool subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBridgedGroups) const;
|
bool subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBridgedGroups) const;
|
||||||
@@ -133,27 +175,26 @@ public:
|
|||||||
void multicastUnsubscribe(const MulticastGroup &mg);
|
void multicastUnsubscribe(const MulticastGroup &mg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Announce multicast groups to a peer if that peer is authorized on this network
|
* Handle an inbound network config chunk
|
||||||
*
|
*
|
||||||
* @param peer Peer to try to announce multicast groups to
|
* This is called from IncomingPacket to handle incoming network config
|
||||||
* @return True if peer was authorized and groups were announced
|
* chunks via OK(NETWORK_CONFIG_REQUEST) or NETWORK_CONFIG. It verifies
|
||||||
|
* each chunk and once assembled applies the configuration.
|
||||||
|
*
|
||||||
|
* @param packetId Packet ID or 0 if none (e.g. via cluster path)
|
||||||
|
* @param source Address of sender of chunk or NULL if none (e.g. via cluster path)
|
||||||
|
* @param chunk Buffer containing chunk
|
||||||
|
* @param ptr Index of chunk and related fields in packet
|
||||||
|
* @return Update ID if update was fully assembled and accepted or 0 otherwise
|
||||||
*/
|
*/
|
||||||
bool tryAnnounceMulticastGroupsTo(const SharedPtr<Peer> &peer);
|
uint64_t handleConfigChunk(const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply a NetworkConfig to this network
|
* Set network configuration
|
||||||
*
|
|
||||||
* @param conf Configuration in NetworkConfig form
|
|
||||||
* @return True if configuration was accepted
|
|
||||||
*/
|
|
||||||
bool applyConfiguration(const NetworkConfig &conf);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set or update this network's configuration
|
|
||||||
*
|
*
|
||||||
* @param nconf Network configuration
|
* @param nconf Network configuration
|
||||||
* @param saveToDisk IF true (default), write config to disk
|
* @param saveToDisk Save to disk? Used during loading, should usually be true otherwise.
|
||||||
* @return 0 -- rejected, 1 -- accepted but not new, 2 -- accepted new config
|
* @return 0 == bad, 1 == accepted but duplicate/unchanged, 2 == accepted and new
|
||||||
*/
|
*/
|
||||||
int setConfiguration(const NetworkConfig &nconf,bool saveToDisk);
|
int setConfiguration(const NetworkConfig &nconf,bool saveToDisk);
|
||||||
|
|
||||||
@@ -167,7 +208,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set netconf failure to 'not found' -- called by PacketDecider when controller reports this
|
* Set netconf failure to 'not found' -- called by IncomingPacket when controller reports this
|
||||||
*/
|
*/
|
||||||
inline void setNotFound()
|
inline void setNotFound()
|
||||||
{
|
{
|
||||||
@@ -181,73 +222,24 @@ public:
|
|||||||
void requestConfiguration();
|
void requestConfiguration();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param peer Peer to check
|
* Determine whether this peer is permitted to communicate on this network
|
||||||
* @return True if peer is allowed to communicate on this network
|
|
||||||
*/
|
*/
|
||||||
inline bool isAllowed(const SharedPtr<Peer> &peer) const
|
bool gate(const SharedPtr<Peer> &peer);
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
return _isAllowed(peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform cleanup and possibly save state
|
* Do periodic cleanup and housekeeping tasks
|
||||||
*/
|
*/
|
||||||
void clean();
|
void clean();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Time of last updated configuration or 0 if none
|
* Push state to members such as multicast group memberships and latest COM (if needed)
|
||||||
*/
|
*/
|
||||||
inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
|
inline void sendUpdatesToMembers()
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Status of this network
|
|
||||||
*/
|
|
||||||
inline ZT_VirtualNetworkStatus status() const
|
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_lock);
|
||||||
return _status();
|
_sendUpdatesToMembers((const MulticastGroup *)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ec Buffer to fill with externally-visible network configuration
|
|
||||||
*/
|
|
||||||
inline void externalConfig(ZT_VirtualNetworkConfig *ec) const
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
_externalConfig(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get current network config
|
|
||||||
*
|
|
||||||
* This returns a const reference to the network config in place, which is safe
|
|
||||||
* to concurrently access but *may* change during access. Normally this isn't a
|
|
||||||
* problem, but if it is use configCopy().
|
|
||||||
*
|
|
||||||
* @return Network configuration (may be a null config if we don't have one yet)
|
|
||||||
*/
|
|
||||||
inline const NetworkConfig &config() const { return _config; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return A thread-safe copy of our NetworkConfig instead of a const reference
|
|
||||||
*/
|
|
||||||
inline NetworkConfig configCopy() const
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
return _config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return True if this network has a valid config
|
|
||||||
*/
|
|
||||||
inline bool hasConfig() const { return (_config); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Ethernet MAC address for this network's local interface
|
|
||||||
*/
|
|
||||||
inline const MAC &mac() const throw() { return _mac; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the node on this network that has this MAC behind it (if any)
|
* Find the node on this network that has this MAC behind it (if any)
|
||||||
*
|
*
|
||||||
@@ -258,9 +250,7 @@ public:
|
|||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_lock);
|
||||||
const Address *const br = _remoteBridgeRoutes.get(mac);
|
const Address *const br = _remoteBridgeRoutes.get(mac);
|
||||||
if (br)
|
return ((br) ? *br : Address());
|
||||||
return *br;
|
|
||||||
return Address();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -279,6 +269,61 @@ public:
|
|||||||
*/
|
*/
|
||||||
void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now);
|
void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a credential and learn it if it passes certificate and other checks
|
||||||
|
*/
|
||||||
|
Membership::AddCredentialResult addCredential(const CertificateOfMembership &com);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a credential and learn it if it passes certificate and other checks
|
||||||
|
*/
|
||||||
|
inline Membership::AddCredentialResult addCredential(const Capability &cap)
|
||||||
|
{
|
||||||
|
if (cap.networkId() != _id)
|
||||||
|
return Membership::ADD_REJECTED;
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
return _membership(cap.issuedTo()).addCredential(RR,_config,cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a credential and learn it if it passes certificate and other checks
|
||||||
|
*/
|
||||||
|
inline Membership::AddCredentialResult addCredential(const Tag &tag)
|
||||||
|
{
|
||||||
|
if (tag.networkId() != _id)
|
||||||
|
return Membership::ADD_REJECTED;
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
return _membership(tag.issuedTo()).addCredential(RR,_config,tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a credential and learn it if it passes certificate and other checks
|
||||||
|
*/
|
||||||
|
Membership::AddCredentialResult addCredential(const Address &sentFrom,const Revocation &rev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a credential and learn it if it passes certificate and other checks
|
||||||
|
*/
|
||||||
|
inline Membership::AddCredentialResult addCredential(const CertificateOfOwnership &coo)
|
||||||
|
{
|
||||||
|
if (coo.networkId() != _id)
|
||||||
|
return Membership::ADD_REJECTED;
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
return _membership(coo.issuedTo()).addCredential(RR,_config,coo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force push credentials (COM, etc.) to a peer now
|
||||||
|
*
|
||||||
|
* @param to Destination peer address
|
||||||
|
* @param now Current time
|
||||||
|
*/
|
||||||
|
inline void pushCredentialsNow(const Address &to,const uint64_t now)
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
_membership(to).pushCredentials(RR,now,to,_config,-1,true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy this network
|
* Destroy this network
|
||||||
*
|
*
|
||||||
@@ -289,39 +334,56 @@ public:
|
|||||||
void destroy();
|
void destroy();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Pointer to user PTR (modifiable user ptr used in API)
|
* Get this network's config for export via the ZT core API
|
||||||
|
*
|
||||||
|
* @param ec Buffer to fill with externally-visible network configuration
|
||||||
|
*/
|
||||||
|
inline void externalConfig(ZT_VirtualNetworkConfig *ec) const
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
_externalConfig(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Externally usable pointer-to-pointer exported via the core API
|
||||||
*/
|
*/
|
||||||
inline void **userPtr() throw() { return &_uPtr; }
|
inline void **userPtr() throw() { return &_uPtr; }
|
||||||
|
|
||||||
inline bool operator==(const Network &n) const throw() { return (_id == n._id); }
|
|
||||||
inline bool operator!=(const Network &n) const throw() { return (_id != n._id); }
|
|
||||||
inline bool operator<(const Network &n) const throw() { return (_id < n._id); }
|
|
||||||
inline bool operator>(const Network &n) const throw() { return (_id > n._id); }
|
|
||||||
inline bool operator<=(const Network &n) const throw() { return (_id <= n._id); }
|
|
||||||
inline bool operator>=(const Network &n) const throw() { return (_id >= n._id); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ZT_VirtualNetworkStatus _status() const;
|
ZT_VirtualNetworkStatus _status() const;
|
||||||
void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked
|
void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked
|
||||||
bool _isAllowed(const SharedPtr<Peer> &peer) const;
|
bool _gate(const SharedPtr<Peer> &peer);
|
||||||
void _announceMulticastGroups();
|
void _sendUpdatesToMembers(const MulticastGroup *const newMulticastGroup);
|
||||||
void _announceMulticastGroupsTo(const SharedPtr<Peer> &peer,const std::vector<MulticastGroup> &allMulticastGroups) const;
|
void _announceMulticastGroupsTo(const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
|
||||||
std::vector<MulticastGroup> _allMulticastGroups() const;
|
std::vector<MulticastGroup> _allMulticastGroups() const;
|
||||||
|
Membership &_membership(const Address &a);
|
||||||
|
|
||||||
const RuntimeEnvironment *RR;
|
const RuntimeEnvironment *const RR;
|
||||||
void *_uPtr;
|
void *_uPtr;
|
||||||
uint64_t _id;
|
const uint64_t _id;
|
||||||
|
uint64_t _lastAnnouncedMulticastGroupsUpstream;
|
||||||
MAC _mac; // local MAC address
|
MAC _mac; // local MAC address
|
||||||
volatile bool _portInitialized;
|
bool _portInitialized;
|
||||||
|
|
||||||
std::vector< MulticastGroup > _myMulticastGroups; // multicast groups that we belong to (according to tap)
|
std::vector< MulticastGroup > _myMulticastGroups; // multicast groups that we belong to (according to tap)
|
||||||
Hashtable< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups that seem to be behind us and when we last saw them (if we are a bridge)
|
Hashtable< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups that seem to be behind us and when we last saw them (if we are a bridge)
|
||||||
Hashtable< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges)
|
Hashtable< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges)
|
||||||
|
|
||||||
NetworkConfig _config;
|
NetworkConfig _config;
|
||||||
volatile uint64_t _lastConfigUpdate;
|
uint64_t _lastConfigUpdate;
|
||||||
|
|
||||||
volatile bool _destroyed;
|
struct _IncomingConfigChunk
|
||||||
|
{
|
||||||
|
uint64_t ts;
|
||||||
|
uint64_t updateId;
|
||||||
|
uint64_t haveChunkIds[ZT_NETWORK_MAX_UPDATE_CHUNKS];
|
||||||
|
unsigned long haveChunks;
|
||||||
|
unsigned long haveBytes;
|
||||||
|
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> data;
|
||||||
|
};
|
||||||
|
_IncomingConfigChunk _incomingConfigChunks[ZT_NETWORK_MAX_INCOMING_UPDATES];
|
||||||
|
|
||||||
|
bool _destroyed;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
NETCONF_FAILURE_NONE,
|
NETCONF_FAILURE_NONE,
|
||||||
@@ -329,7 +391,9 @@ private:
|
|||||||
NETCONF_FAILURE_NOT_FOUND,
|
NETCONF_FAILURE_NOT_FOUND,
|
||||||
NETCONF_FAILURE_INIT_FAILED
|
NETCONF_FAILURE_INIT_FAILED
|
||||||
} _netconfFailure;
|
} _netconfFailure;
|
||||||
volatile int _portError; // return value from port config callback
|
int _portError; // return value from port config callback
|
||||||
|
|
||||||
|
Hashtable<Address,Membership> _memberships;
|
||||||
|
|
||||||
Mutex _lock;
|
Mutex _lock;
|
||||||
|
|
||||||
|
|||||||
@@ -18,15 +18,17 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "NetworkConfig.hpp"
|
#include "NetworkConfig.hpp"
|
||||||
#include "Utils.hpp"
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const
|
bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const
|
||||||
{
|
{
|
||||||
Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> tmp;
|
Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
||||||
|
|
||||||
|
try {
|
||||||
d.clear();
|
d.clear();
|
||||||
|
|
||||||
// Try to put the more human-readable fields first
|
// Try to put the more human-readable fields first
|
||||||
@@ -34,6 +36,7 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
|
|||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION)) return false;
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION)) return false;
|
||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId)) return false;
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId)) return false;
|
||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) return false;
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) return false;
|
||||||
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,this->credentialTimeMaxDelta)) return false;
|
||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) return false;
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) return false;
|
||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo)) return false;
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo)) return false;
|
||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags)) return false;
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags)) return false;
|
||||||
@@ -81,9 +84,9 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
|
|||||||
if (((int)lastrt < 32)||(lastrt == ZT_NETWORK_RULE_MATCH_ETHERTYPE)) {
|
if (((int)lastrt < 32)||(lastrt == ZT_NETWORK_RULE_MATCH_ETHERTYPE)) {
|
||||||
if (ets.length() > 0)
|
if (ets.length() > 0)
|
||||||
ets.push_back(',');
|
ets.push_back(',');
|
||||||
char tmp[16];
|
char tmp2[16];
|
||||||
Utils::snprintf(tmp,sizeof(tmp),"%x",et);
|
Utils::snprintf(tmp2,sizeof(tmp2),"%x",et);
|
||||||
ets.append(tmp);
|
ets.append(tmp2);
|
||||||
}
|
}
|
||||||
et = 0;
|
et = 0;
|
||||||
}
|
}
|
||||||
@@ -108,158 +111,75 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
|
|||||||
if (ab.length() > 0) {
|
if (ab.length() > 0) {
|
||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,ab.c_str())) return false;
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,ab.c_str())) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Relay> rvec(this->relays());
|
|
||||||
std::string rl;
|
|
||||||
for(std::vector<Relay>::const_iterator i(rvec.begin());i!=rvec.end();++i) {
|
|
||||||
if (rl.length() > 0)
|
|
||||||
rl.push_back(',');
|
|
||||||
rl.append(i->address.toString());
|
|
||||||
if (i->phy4) {
|
|
||||||
rl.push_back(';');
|
|
||||||
rl.append(i->phy4.toString());
|
|
||||||
} else if (i->phy6) {
|
|
||||||
rl.push_back(';');
|
|
||||||
rl.append(i->phy6.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rl.length() > 0) {
|
|
||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD,rl.c_str())) return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||||
|
|
||||||
// Then add binary blobs
|
// Then add binary blobs
|
||||||
|
|
||||||
if (this->com) {
|
if (this->com) {
|
||||||
tmp.clear();
|
tmp->clear();
|
||||||
this->com.serialize(tmp);
|
this->com.serialize(*tmp);
|
||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,tmp)) return false;
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp.clear();
|
tmp->clear();
|
||||||
for(unsigned int i=0;i<this->specialistCount;++i) {
|
for(unsigned int i=0;i<this->capabilityCount;++i)
|
||||||
tmp.append((uint64_t)this->specialists[i]);
|
this->capabilities[i].serialize(*tmp);
|
||||||
}
|
if (tmp->size()) {
|
||||||
if (tmp.size()) {
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,*tmp)) return false;
|
||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,tmp)) return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp.clear();
|
tmp->clear();
|
||||||
|
for(unsigned int i=0;i<this->tagCount;++i)
|
||||||
|
this->tags[i].serialize(*tmp);
|
||||||
|
if (tmp->size()) {
|
||||||
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TAGS,*tmp)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp->clear();
|
||||||
|
for(unsigned int i=0;i<this->certificateOfOwnershipCount;++i)
|
||||||
|
this->certificatesOfOwnership[i].serialize(*tmp);
|
||||||
|
if (tmp->size()) {
|
||||||
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,*tmp)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp->clear();
|
||||||
|
for(unsigned int i=0;i<this->specialistCount;++i)
|
||||||
|
tmp->append((uint64_t)this->specialists[i]);
|
||||||
|
if (tmp->size()) {
|
||||||
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,*tmp)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp->clear();
|
||||||
for(unsigned int i=0;i<this->routeCount;++i) {
|
for(unsigned int i=0;i<this->routeCount;++i) {
|
||||||
reinterpret_cast<const InetAddress *>(&(this->routes[i].target))->serialize(tmp);
|
reinterpret_cast<const InetAddress *>(&(this->routes[i].target))->serialize(*tmp);
|
||||||
reinterpret_cast<const InetAddress *>(&(this->routes[i].via))->serialize(tmp);
|
reinterpret_cast<const InetAddress *>(&(this->routes[i].via))->serialize(*tmp);
|
||||||
tmp.append((uint16_t)this->routes[i].flags);
|
tmp->append((uint16_t)this->routes[i].flags);
|
||||||
tmp.append((uint16_t)this->routes[i].metric);
|
tmp->append((uint16_t)this->routes[i].metric);
|
||||||
}
|
}
|
||||||
if (tmp.size()) {
|
if (tmp->size()) {
|
||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,tmp)) return false;
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp.clear();
|
tmp->clear();
|
||||||
for(unsigned int i=0;i<this->staticIpCount;++i) {
|
for(unsigned int i=0;i<this->staticIpCount;++i)
|
||||||
this->staticIps[i].serialize(tmp);
|
this->staticIps[i].serialize(*tmp);
|
||||||
}
|
if (tmp->size()) {
|
||||||
if (tmp.size()) {
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) return false;
|
||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,tmp)) return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp.clear();
|
if (this->ruleCount) {
|
||||||
for(unsigned int i=0;i<this->pinnedCount;++i) {
|
tmp->clear();
|
||||||
this->pinned[i].zt.appendTo(tmp);
|
Capability::serializeRules(*tmp,rules,ruleCount);
|
||||||
this->pinned[i].phy.serialize(tmp);
|
if (tmp->size()) {
|
||||||
|
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) return false;
|
||||||
}
|
}
|
||||||
if (tmp.size()) {
|
|
||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PINNED,tmp)) return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp.clear();
|
delete tmp;
|
||||||
for(unsigned int i=0;i<this->ruleCount;++i) {
|
} catch ( ... ) {
|
||||||
tmp.append((uint8_t)rules[i].t);
|
delete tmp;
|
||||||
switch((ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f)) {
|
throw;
|
||||||
//case ZT_NETWORK_RULE_ACTION_DROP:
|
|
||||||
//case ZT_NETWORK_RULE_ACTION_ACCEPT:
|
|
||||||
default:
|
|
||||||
tmp.append((uint8_t)0);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_ACTION_TEE:
|
|
||||||
case ZT_NETWORK_RULE_ACTION_REDIRECT:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
|
|
||||||
tmp.append((uint8_t)5);
|
|
||||||
Address(rules[i].v.zt).appendTo(tmp);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_VLAN_ID:
|
|
||||||
tmp.append((uint8_t)2);
|
|
||||||
tmp.append((uint16_t)rules[i].v.vlanId);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
|
|
||||||
tmp.append((uint8_t)1);
|
|
||||||
tmp.append((uint8_t)rules[i].v.vlanPcp);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
|
|
||||||
tmp.append((uint8_t)1);
|
|
||||||
tmp.append((uint8_t)rules[i].v.vlanDei);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
|
|
||||||
tmp.append((uint8_t)2);
|
|
||||||
tmp.append((uint16_t)rules[i].v.etherType);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_MAC_DEST:
|
|
||||||
tmp.append((uint8_t)6);
|
|
||||||
tmp.append(rules[i].v.mac,6);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
|
|
||||||
tmp.append((uint8_t)5);
|
|
||||||
tmp.append(&(rules[i].v.ipv4.ip),4);
|
|
||||||
tmp.append((uint8_t)rules[i].v.ipv4.mask);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
|
|
||||||
tmp.append((uint8_t)17);
|
|
||||||
tmp.append(rules[i].v.ipv6.ip,16);
|
|
||||||
tmp.append((uint8_t)rules[i].v.ipv6.mask);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IP_TOS:
|
|
||||||
tmp.append((uint8_t)1);
|
|
||||||
tmp.append((uint8_t)rules[i].v.ipTos);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
|
|
||||||
tmp.append((uint8_t)1);
|
|
||||||
tmp.append((uint8_t)rules[i].v.ipProtocol);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
|
|
||||||
tmp.append((uint8_t)4);
|
|
||||||
tmp.append((uint16_t)rules[i].v.port[0]);
|
|
||||||
tmp.append((uint16_t)rules[i].v.port[1]);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
|
|
||||||
tmp.append((uint8_t)8);
|
|
||||||
tmp.append((uint64_t)rules[i].v.characteristics);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
|
|
||||||
tmp.append((uint8_t)4);
|
|
||||||
tmp.append((uint16_t)rules[i].v.frameSize[0]);
|
|
||||||
tmp.append((uint16_t)rules[i].v.frameSize[1]);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
|
|
||||||
tmp.append((uint8_t)8);
|
|
||||||
tmp.append((uint32_t)rules[i].v.tcpseq[0]);
|
|
||||||
tmp.append((uint32_t)rules[i].v.tcpseq[1]);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_COM_FIELD_GE:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_COM_FIELD_LE:
|
|
||||||
tmp.append((uint8_t)16);
|
|
||||||
tmp.append((uint64_t)rules[i].v.comIV[0]);
|
|
||||||
tmp.append((uint64_t)rules[i].v.comIV[1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tmp.size()) {
|
|
||||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,tmp)) return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -267,26 +187,32 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
|
|||||||
|
|
||||||
bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d)
|
bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d)
|
||||||
{
|
{
|
||||||
try {
|
Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
||||||
Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> tmp;
|
|
||||||
char tmp2[ZT_NETWORKCONFIG_DICT_CAPACITY];
|
|
||||||
|
|
||||||
|
try {
|
||||||
memset(this,0,sizeof(NetworkConfig));
|
memset(this,0,sizeof(NetworkConfig));
|
||||||
|
|
||||||
// Fields that are always present, new or old
|
// Fields that are always present, new or old
|
||||||
this->networkId = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,0);
|
this->networkId = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,0);
|
||||||
if (!this->networkId)
|
if (!this->networkId) {
|
||||||
|
delete tmp;
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
this->timestamp = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,0);
|
this->timestamp = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,0);
|
||||||
|
this->credentialTimeMaxDelta = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,0);
|
||||||
this->revision = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REVISION,0);
|
this->revision = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REVISION,0);
|
||||||
this->issuedTo = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,0);
|
this->issuedTo = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,0);
|
||||||
if (!this->issuedTo)
|
if (!this->issuedTo) {
|
||||||
|
delete tmp;
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
this->multicastLimit = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,0);
|
this->multicastLimit = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,0);
|
||||||
d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name,sizeof(this->name));
|
d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name,sizeof(this->name));
|
||||||
|
|
||||||
if (d.getUI(ZT_NETWORKCONFIG_DICT_KEY_VERSION,0) < 6) {
|
if (d.getUI(ZT_NETWORKCONFIG_DICT_KEY_VERSION,0) < 6) {
|
||||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||||
|
char tmp2[1024];
|
||||||
|
|
||||||
// Decode legacy fields if version is old
|
// Decode legacy fields if version is old
|
||||||
if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD))
|
if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD))
|
||||||
this->flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING;
|
this->flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING;
|
||||||
@@ -338,36 +264,11 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
|
|||||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,tmp2,sizeof(tmp2)) > 0) {
|
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||||
char *saveptr = (char *)0;
|
char *saveptr = (char *)0;
|
||||||
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
||||||
this->addSpecialist(Address(f),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
|
this->addSpecialist(Address(Utils::hexStrToU64(f)),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD,tmp2,sizeof(tmp2)) > 0) {
|
|
||||||
char *saveptr = (char *)0;
|
|
||||||
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
|
||||||
char tmp3[256];
|
|
||||||
Utils::scopy(tmp3,sizeof(tmp3),f);
|
|
||||||
|
|
||||||
InetAddress phy;
|
|
||||||
char *semi = tmp3;
|
|
||||||
while (*semi) {
|
|
||||||
if (*semi == ';') {
|
|
||||||
*semi = (char)0;
|
|
||||||
++semi;
|
|
||||||
phy = InetAddress(semi);
|
|
||||||
} else ++semi;
|
|
||||||
}
|
|
||||||
Address zt(tmp3);
|
|
||||||
|
|
||||||
this->addSpecialist(zt,ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY);
|
|
||||||
if ((phy)&&(this->pinnedCount < ZT_MAX_NETWORK_PINNED)) {
|
|
||||||
this->pinned[this->pinnedCount].zt = zt;
|
|
||||||
this->pinned[this->pinnedCount].phy = phy;
|
|
||||||
++this->pinnedCount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
delete tmp;
|
||||||
return false;
|
return false;
|
||||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||||
} else {
|
} else {
|
||||||
@@ -375,116 +276,76 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
|
|||||||
this->flags = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,0);
|
this->flags = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,0);
|
||||||
this->type = (ZT_VirtualNetworkType)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)ZT_NETWORK_TYPE_PRIVATE);
|
this->type = (ZT_VirtualNetworkType)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)ZT_NETWORK_TYPE_PRIVATE);
|
||||||
|
|
||||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,tmp)) {
|
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp))
|
||||||
this->com.deserialize(tmp,0);
|
this->com.deserialize(*tmp,0);
|
||||||
|
|
||||||
|
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,*tmp)) {
|
||||||
|
try {
|
||||||
|
unsigned int p = 0;
|
||||||
|
while (p < tmp->size()) {
|
||||||
|
Capability cap;
|
||||||
|
p += cap.deserialize(*tmp,p);
|
||||||
|
this->capabilities[this->capabilityCount++] = cap;
|
||||||
|
}
|
||||||
|
} catch ( ... ) {}
|
||||||
|
std::sort(&(this->capabilities[0]),&(this->capabilities[this->capabilityCount]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,tmp)) {
|
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_TAGS,*tmp)) {
|
||||||
|
try {
|
||||||
unsigned int p = 0;
|
unsigned int p = 0;
|
||||||
while (((p + 8) <= tmp.size())&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS)) {
|
while (p < tmp->size()) {
|
||||||
this->specialists[this->specialistCount++] = tmp.at<uint64_t>(p);
|
Tag tag;
|
||||||
|
p += tag.deserialize(*tmp,p);
|
||||||
|
this->tags[this->tagCount++] = tag;
|
||||||
|
}
|
||||||
|
} catch ( ... ) {}
|
||||||
|
std::sort(&(this->tags[0]),&(this->tags[this->tagCount]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,*tmp)) {
|
||||||
|
unsigned int p = 0;
|
||||||
|
while (p < tmp->size()) {
|
||||||
|
if (certificateOfOwnershipCount < ZT_MAX_CERTIFICATES_OF_OWNERSHIP)
|
||||||
|
p += certificatesOfOwnership[certificateOfOwnershipCount++].deserialize(*tmp,p);
|
||||||
|
else {
|
||||||
|
CertificateOfOwnership foo;
|
||||||
|
p += foo.deserialize(*tmp,p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,*tmp)) {
|
||||||
|
unsigned int p = 0;
|
||||||
|
while ((p + 8) <= tmp->size()) {
|
||||||
|
if (specialistCount < ZT_MAX_NETWORK_SPECIALISTS)
|
||||||
|
this->specialists[this->specialistCount++] = tmp->at<uint64_t>(p);
|
||||||
p += 8;
|
p += 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,tmp)) {
|
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) {
|
||||||
unsigned int p = 0;
|
unsigned int p = 0;
|
||||||
while ((p < tmp.size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
|
while ((p < tmp->size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
|
||||||
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].target))->deserialize(tmp,p);
|
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].target))->deserialize(*tmp,p);
|
||||||
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].via))->deserialize(tmp,p);
|
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].via))->deserialize(*tmp,p);
|
||||||
this->routes[this->routeCount].flags = tmp.at<uint16_t>(p); p += 2;
|
this->routes[this->routeCount].flags = tmp->at<uint16_t>(p); p += 2;
|
||||||
this->routes[this->routeCount].metric = tmp.at<uint16_t>(p); p += 2;
|
this->routes[this->routeCount].metric = tmp->at<uint16_t>(p); p += 2;
|
||||||
++this->routeCount;
|
++this->routeCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,tmp)) {
|
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) {
|
||||||
unsigned int p = 0;
|
unsigned int p = 0;
|
||||||
while ((p < tmp.size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
|
while ((p < tmp->size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
|
||||||
p += this->staticIps[this->staticIpCount++].deserialize(tmp,p);
|
p += this->staticIps[this->staticIpCount++].deserialize(*tmp,p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_PINNED,tmp)) {
|
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) {
|
||||||
|
this->ruleCount = 0;
|
||||||
unsigned int p = 0;
|
unsigned int p = 0;
|
||||||
while ((p < tmp.size())&&(pinnedCount < ZT_MAX_NETWORK_PINNED)) {
|
Capability::deserializeRules(*tmp,p,this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES);
|
||||||
this->pinned[this->pinnedCount].zt.setTo(tmp.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
|
|
||||||
p += this->pinned[this->pinnedCount].phy.deserialize(tmp,p);
|
|
||||||
++this->pinnedCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,tmp)) {
|
|
||||||
unsigned int p = 0;
|
|
||||||
while ((p < tmp.size())&&(ruleCount < ZT_MAX_NETWORK_RULES)) {
|
|
||||||
rules[ruleCount].t = (uint8_t)tmp[p++];
|
|
||||||
unsigned int fieldLen = (unsigned int)tmp[p++];
|
|
||||||
switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x7f)) {
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_ACTION_TEE:
|
|
||||||
case ZT_NETWORK_RULE_ACTION_REDIRECT:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
|
|
||||||
rules[ruleCount].v.zt = Address(tmp.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt();
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_VLAN_ID:
|
|
||||||
rules[ruleCount].v.vlanId = tmp.at<uint16_t>(p);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
|
|
||||||
rules[ruleCount].v.vlanPcp = (uint8_t)tmp[p];
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
|
|
||||||
rules[ruleCount].v.vlanDei = (uint8_t)tmp[p];
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
|
|
||||||
rules[ruleCount].v.etherType = tmp.at<uint16_t>(p);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_MAC_DEST:
|
|
||||||
memcpy(rules[ruleCount].v.mac,tmp.field(p,6),6);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
|
|
||||||
memcpy(&(rules[ruleCount].v.ipv4.ip),tmp.field(p,4),4);
|
|
||||||
rules[ruleCount].v.ipv4.mask = (uint8_t)tmp[p + 4];
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
|
|
||||||
memcpy(rules[ruleCount].v.ipv6.ip,tmp.field(p,16),16);
|
|
||||||
rules[ruleCount].v.ipv6.mask = (uint8_t)tmp[p + 16];
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IP_TOS:
|
|
||||||
rules[ruleCount].v.ipTos = (uint8_t)tmp[p];
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
|
|
||||||
rules[ruleCount].v.ipProtocol = (uint8_t)tmp[p];
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
|
|
||||||
rules[ruleCount].v.port[0] = tmp.at<uint16_t>(p);
|
|
||||||
rules[ruleCount].v.port[1] = tmp.at<uint16_t>(p + 2);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
|
|
||||||
rules[ruleCount].v.characteristics = tmp.at<uint64_t>(p);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
|
|
||||||
rules[ruleCount].v.frameSize[0] = tmp.at<uint16_t>(p);
|
|
||||||
rules[ruleCount].v.frameSize[0] = tmp.at<uint16_t>(p + 2);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
|
|
||||||
rules[ruleCount].v.tcpseq[0] = tmp.at<uint32_t>(p);
|
|
||||||
rules[ruleCount].v.tcpseq[1] = tmp.at<uint32_t>(p + 4);
|
|
||||||
break;
|
|
||||||
case ZT_NETWORK_RULE_MATCH_COM_FIELD_GE:
|
|
||||||
case ZT_NETWORK_RULE_MATCH_COM_FIELD_LE:
|
|
||||||
rules[ruleCount].v.comIV[0] = tmp.at<uint64_t>(p);
|
|
||||||
rules[ruleCount].v.comIV[1] = tmp.at<uint64_t>(p + 8);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p += fieldLen;
|
|
||||||
++ruleCount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,8 +353,10 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
|
|||||||
//dump();
|
//dump();
|
||||||
//printf("~~~\n");
|
//printf("~~~\n");
|
||||||
|
|
||||||
|
delete tmp;
|
||||||
return true;
|
return true;
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
|
delete tmp;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,28 @@
|
|||||||
#include "MulticastGroup.hpp"
|
#include "MulticastGroup.hpp"
|
||||||
#include "Address.hpp"
|
#include "Address.hpp"
|
||||||
#include "CertificateOfMembership.hpp"
|
#include "CertificateOfMembership.hpp"
|
||||||
|
#include "CertificateOfOwnership.hpp"
|
||||||
|
#include "Capability.hpp"
|
||||||
|
#include "Tag.hpp"
|
||||||
#include "Dictionary.hpp"
|
#include "Dictionary.hpp"
|
||||||
|
#include "Identity.hpp"
|
||||||
|
#include "Utils.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.
|
||||||
|
*/
|
||||||
|
#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA 7200000ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA 185000ULL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag: allow passive bridging (experimental)
|
* Flag: allow passive bridging (experimental)
|
||||||
@@ -53,9 +74,14 @@
|
|||||||
#define ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION 0x0000000000000004ULL
|
#define ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION 0x0000000000000004ULL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Device is a network preferred relay
|
* Flag: result of unrecognized MATCH entries in a rules table: match if set, no-match if clear
|
||||||
*/
|
*/
|
||||||
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY 0x0000010000000000ULL
|
#define ZT_NETWORKCONFIG_FLAG_RULES_RESULT_OF_UNSUPPORTED_MATCH 0x0000000000000008ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag: disable frame compression
|
||||||
|
*/
|
||||||
|
#define ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION 0x0000000000000010ULL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Device is an active bridge
|
* Device is an active bridge
|
||||||
@@ -63,26 +89,57 @@
|
|||||||
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE 0x0000020000000000ULL
|
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE 0x0000020000000000ULL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An anchor is a device that is willing to be one and has been online/stable for a long time on this network
|
* Anchors are stable devices on this network that can cache multicast info, etc.
|
||||||
*/
|
*/
|
||||||
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR 0x0000040000000000ULL
|
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR 0x0000040000000000ULL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device can send CIRCUIT_TESTs for this network
|
||||||
|
*/
|
||||||
|
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_CIRCUIT_TESTER 0x0000080000000000ULL
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
// Maximum size of a network config dictionary (can be increased)
|
// Dictionary capacity needed for max size network config
|
||||||
#define ZT_NETWORKCONFIG_DICT_CAPACITY 8194
|
#define ZT_NETWORKCONFIG_DICT_CAPACITY (1024 + (sizeof(ZT_VirtualNetworkRule) * ZT_MAX_NETWORK_RULES) + (sizeof(Capability) * ZT_MAX_NETWORK_CAPABILITIES) + (sizeof(Tag) * ZT_MAX_NETWORK_TAGS) + (sizeof(CertificateOfOwnership) * ZT_MAX_CERTIFICATES_OF_OWNERSHIP))
|
||||||
|
|
||||||
|
// Dictionary capacity needed for max size network meta-data
|
||||||
|
#define ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY 1024
|
||||||
|
|
||||||
// Network config version
|
// Network config version
|
||||||
#define ZT_NETWORKCONFIG_VERSION 6
|
#define ZT_NETWORKCONFIG_VERSION 7
|
||||||
|
|
||||||
// Fields for meta-data sent with network config requests
|
// Fields for meta-data sent with network config requests
|
||||||
|
|
||||||
|
// Network config version
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION "v"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION "v"
|
||||||
|
// Protocol version (see Packet.hpp)
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION "pv"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION "pv"
|
||||||
|
// Software vendor
|
||||||
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_VENDOR "vend"
|
||||||
|
// Software major version
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION "majv"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION "majv"
|
||||||
|
// Software minor version
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION "minv"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION "minv"
|
||||||
|
// Software revision
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION "revv"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION "revv"
|
||||||
|
// Rules engine revision
|
||||||
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV "revr"
|
||||||
|
// Maximum number of rules per network this node can accept
|
||||||
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_RULES "mr"
|
||||||
|
// Maximum number of capabilities this node can accept
|
||||||
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_CAPABILITIES "mc"
|
||||||
|
// Maximum number of rules per capability this node can accept
|
||||||
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_CAPABILITY_RULES "mcr"
|
||||||
|
// Maximum number of tags this node can accept
|
||||||
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_TAGS "mt"
|
||||||
|
// Network join authorization token (if any)
|
||||||
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH "a"
|
||||||
|
// Network configuration meta-data flags
|
||||||
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_FLAGS "f"
|
||||||
|
|
||||||
// These dictionary keys are short so they don't take up much room.
|
// These dictionary keys are short so they don't take up much room.
|
||||||
|
// By convention we use upper case for binary blobs, but it doesn't really matter.
|
||||||
|
|
||||||
// network config version
|
// network config version
|
||||||
#define ZT_NETWORKCONFIG_DICT_KEY_VERSION "v"
|
#define ZT_NETWORKCONFIG_DICT_KEY_VERSION "v"
|
||||||
@@ -102,6 +159,8 @@ namespace ZeroTier {
|
|||||||
#define ZT_NETWORKCONFIG_DICT_KEY_TYPE "t"
|
#define ZT_NETWORKCONFIG_DICT_KEY_TYPE "t"
|
||||||
// text
|
// text
|
||||||
#define ZT_NETWORKCONFIG_DICT_KEY_NAME "n"
|
#define ZT_NETWORKCONFIG_DICT_KEY_NAME "n"
|
||||||
|
// credential time max delta in ms
|
||||||
|
#define ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA "ctmd"
|
||||||
// binary serialized certificate of membership
|
// binary serialized certificate of membership
|
||||||
#define ZT_NETWORKCONFIG_DICT_KEY_COM "C"
|
#define ZT_NETWORKCONFIG_DICT_KEY_COM "C"
|
||||||
// specialists (binary array of uint64_t)
|
// specialists (binary array of uint64_t)
|
||||||
@@ -110,10 +169,16 @@ namespace ZeroTier {
|
|||||||
#define ZT_NETWORKCONFIG_DICT_KEY_ROUTES "RT"
|
#define ZT_NETWORKCONFIG_DICT_KEY_ROUTES "RT"
|
||||||
// static IPs (binary blob)
|
// static IPs (binary blob)
|
||||||
#define ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS "I"
|
#define ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS "I"
|
||||||
// pinned address physical route mappings (binary blob)
|
|
||||||
#define ZT_NETWORKCONFIG_DICT_KEY_PINNED "P"
|
|
||||||
// rules (binary blob)
|
// rules (binary blob)
|
||||||
#define ZT_NETWORKCONFIG_DICT_KEY_RULES "R"
|
#define ZT_NETWORKCONFIG_DICT_KEY_RULES "R"
|
||||||
|
// capabilities (binary blobs)
|
||||||
|
#define ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES "CAP"
|
||||||
|
// tags (binary blobs)
|
||||||
|
#define ZT_NETWORKCONFIG_DICT_KEY_TAGS "TAG"
|
||||||
|
// tags (binary blobs)
|
||||||
|
#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP "COO"
|
||||||
|
// curve25519 signature
|
||||||
|
#define ZT_NETWORKCONFIG_DICT_KEY_SIGNATURE "C25519"
|
||||||
|
|
||||||
// Legacy fields -- these are obsoleted but are included when older clients query
|
// Legacy fields -- these are obsoleted but are included when older clients query
|
||||||
|
|
||||||
@@ -138,6 +203,8 @@ namespace ZeroTier {
|
|||||||
// node;IP/port[,node;IP/port]
|
// node;IP/port[,node;IP/port]
|
||||||
#define ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD "rl"
|
#define ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD "rl"
|
||||||
|
|
||||||
|
// End legacy fields
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Network configuration received from network controller nodes
|
* Network configuration received from network controller nodes
|
||||||
*
|
*
|
||||||
@@ -147,58 +214,6 @@ namespace ZeroTier {
|
|||||||
class NetworkConfig
|
class NetworkConfig
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* Network preferred relay with optional physical endpoint addresses
|
|
||||||
*
|
|
||||||
* This is used by the convenience relays() method.
|
|
||||||
*/
|
|
||||||
struct Relay
|
|
||||||
{
|
|
||||||
Address address;
|
|
||||||
InetAddress phy4,phy6;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an instance of a NetworkConfig for the test network ID
|
|
||||||
*
|
|
||||||
* The test network ID is defined as ZT_TEST_NETWORK_ID. This is a
|
|
||||||
* "fake" network with no real controller and default options.
|
|
||||||
*
|
|
||||||
* @param self This node's ZT address
|
|
||||||
* @return Configuration for test network ID
|
|
||||||
*/
|
|
||||||
static inline NetworkConfig createTestNetworkConfig(const Address &self)
|
|
||||||
{
|
|
||||||
NetworkConfig nc;
|
|
||||||
|
|
||||||
nc.networkId = ZT_TEST_NETWORK_ID;
|
|
||||||
nc.timestamp = 1;
|
|
||||||
nc.revision = 1;
|
|
||||||
nc.issuedTo = self;
|
|
||||||
nc.multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT;
|
|
||||||
nc.flags = ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
|
|
||||||
nc.type = ZT_NETWORK_TYPE_PUBLIC;
|
|
||||||
|
|
||||||
nc.rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
|
|
||||||
nc.ruleCount = 1;
|
|
||||||
|
|
||||||
Utils::snprintf(nc.name,sizeof(nc.name),"ZT_TEST_NETWORK");
|
|
||||||
|
|
||||||
// Make up a V4 IP from 'self' in the 10.0.0.0/8 range -- no
|
|
||||||
// guarantee of uniqueness but collisions are unlikely.
|
|
||||||
uint32_t ip = (uint32_t)((self.toInt() & 0x00ffffff) | 0x0a000000); // 10.x.x.x
|
|
||||||
if ((ip & 0x000000ff) == 0x000000ff) ip ^= 0x00000001; // but not ending in .255
|
|
||||||
if ((ip & 0x000000ff) == 0x00000000) ip ^= 0x00000001; // or .0
|
|
||||||
nc.staticIps[0] = InetAddress(Utils::hton(ip),8);
|
|
||||||
|
|
||||||
// Assign an RFC4193-compliant IPv6 address -- will never collide
|
|
||||||
nc.staticIps[1] = InetAddress::makeIpv6rfc4193(ZT_TEST_NETWORK_ID,self.toInt());
|
|
||||||
|
|
||||||
nc.staticIpCount = 2;
|
|
||||||
|
|
||||||
return nc;
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkConfig()
|
NetworkConfig()
|
||||||
{
|
{
|
||||||
memset(this,0,sizeof(NetworkConfig));
|
memset(this,0,sizeof(NetworkConfig));
|
||||||
@@ -215,26 +230,6 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param etherType Ethernet frame type to check
|
|
||||||
* @return True if allowed on this network
|
|
||||||
*/
|
|
||||||
inline bool permitsEtherType(unsigned int etherType) const
|
|
||||||
{
|
|
||||||
unsigned int et = 0;
|
|
||||||
for(unsigned int i=0;i<ruleCount;++i) {
|
|
||||||
ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f);
|
|
||||||
if (rt == ZT_NETWORK_RULE_MATCH_ETHERTYPE) {
|
|
||||||
et = rules[i].v.etherType;
|
|
||||||
} else if (rt == ZT_NETWORK_RULE_ACTION_ACCEPT) {
|
|
||||||
if ((!et)||(et == etherType))
|
|
||||||
return true;
|
|
||||||
et = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write this network config to a dictionary for transport
|
* Write this network config to a dictionary for transport
|
||||||
*
|
*
|
||||||
@@ -247,7 +242,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Read this network config from a dictionary
|
* Read this network config from a dictionary
|
||||||
*
|
*
|
||||||
* @param d Dictionary
|
* @param d Dictionary (non-const since it might be modified during parse, should not be used after call)
|
||||||
* @return True if dictionary was valid and network config successfully initialized
|
* @return True if dictionary was valid and network config successfully initialized
|
||||||
*/
|
*/
|
||||||
bool fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d);
|
bool fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d);
|
||||||
@@ -267,6 +262,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline bool ndpEmulation() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); }
|
inline bool ndpEmulation() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if frames should not be compressed
|
||||||
|
*/
|
||||||
|
inline bool disableCompression() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION) != 0); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Network type is public (no access control)
|
* @return Network type is public (no access control)
|
||||||
*/
|
*/
|
||||||
@@ -304,40 +304,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get pinned physical address for a given ZeroTier address, if any
|
* @param a Address to check
|
||||||
*
|
* @return True if address is an anchor
|
||||||
* @param zt ZeroTier address
|
|
||||||
* @param af Address family (e.g. AF_INET) or 0 for the first we find of any type
|
|
||||||
* @return Physical address, if any
|
|
||||||
*/
|
*/
|
||||||
inline InetAddress findPinnedAddress(const Address &zt,unsigned int af) const
|
inline bool isAnchor(const Address &a) const
|
||||||
{
|
{
|
||||||
for(unsigned int i=0;i<pinnedCount;++i) {
|
|
||||||
if (pinned[i].zt == zt) {
|
|
||||||
if ((af == 0)||((unsigned int)pinned[i].phy.ss_family == af))
|
|
||||||
return pinned[i].phy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return InetAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This gets network preferred relays with their static physical address if one is defined
|
|
||||||
*
|
|
||||||
* @return Network-preferred relays for this network (if none, only roots will be used)
|
|
||||||
*/
|
|
||||||
inline std::vector<Relay> relays() const
|
|
||||||
{
|
|
||||||
std::vector<Relay> r;
|
|
||||||
for(unsigned int i=0;i<specialistCount;++i) {
|
for(unsigned int i=0;i<specialistCount;++i) {
|
||||||
if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) {
|
if ((a == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR) != 0))
|
||||||
r.push_back(Relay());
|
return true;
|
||||||
r.back().address = specialists[i];
|
|
||||||
r.back().phy4 = findPinnedAddress(r.back().address,AF_INET);
|
|
||||||
r.back().phy6 = findPinnedAddress(r.back().address,AF_INET6);
|
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -356,31 +332,15 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterate through relays efficiently
|
* @param byPeer Address to check
|
||||||
*
|
* @return True if this peer is allowed to do circuit tests on this network (controller is always true)
|
||||||
* @param ptr Value-result parameter -- start by initializing with zero, then call until return is null
|
|
||||||
* @return Address of relay or NULL if no more
|
|
||||||
*/
|
*/
|
||||||
Address nextRelay(unsigned int &ptr) const
|
inline bool circuitTestingAllowed(const Address &byPeer) const
|
||||||
{
|
|
||||||
while (ptr < specialistCount) {
|
|
||||||
if ((specialists[ptr] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) {
|
|
||||||
return Address(specialists[ptr++]);
|
|
||||||
} else {
|
|
||||||
++ptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Address();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param zt ZeroTier address
|
|
||||||
* @return True if this address is a relay
|
|
||||||
*/
|
|
||||||
bool isRelay(const Address &zt) const
|
|
||||||
{
|
{
|
||||||
|
if (byPeer.toInt() == ((networkId >> 24) & 0xffffffffffULL))
|
||||||
|
return true;
|
||||||
for(unsigned int i=0;i<specialistCount;++i) {
|
for(unsigned int i=0;i<specialistCount;++i) {
|
||||||
if ((zt == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0))
|
if ((byPeer == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_CIRCUIT_TESTER) != 0))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -394,39 +354,6 @@ public:
|
|||||||
inline bool operator==(const NetworkConfig &nc) const { return (memcmp(this,&nc,sizeof(NetworkConfig)) == 0); }
|
inline bool operator==(const NetworkConfig &nc) const { return (memcmp(this,&nc,sizeof(NetworkConfig)) == 0); }
|
||||||
inline bool operator!=(const NetworkConfig &nc) const { return (!(*this == nc)); }
|
inline bool operator!=(const NetworkConfig &nc) const { return (!(*this == nc)); }
|
||||||
|
|
||||||
/*
|
|
||||||
inline void dump() const
|
|
||||||
{
|
|
||||||
printf("networkId==%.16llx\n",networkId);
|
|
||||||
printf("timestamp==%llu\n",timestamp);
|
|
||||||
printf("revision==%llu\n",revision);
|
|
||||||
printf("issuedTo==%.10llx\n",issuedTo.toInt());
|
|
||||||
printf("multicastLimit==%u\n",multicastLimit);
|
|
||||||
printf("flags=%.8lx\n",(unsigned long)flags);
|
|
||||||
printf("specialistCount==%u\n",specialistCount);
|
|
||||||
for(unsigned int i=0;i<specialistCount;++i)
|
|
||||||
printf(" specialists[%u]==%.16llx\n",i,specialists[i]);
|
|
||||||
printf("routeCount==%u\n",routeCount);
|
|
||||||
for(unsigned int i=0;i<routeCount;++i) {
|
|
||||||
printf(" routes[i].target==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].target))->toString().c_str());
|
|
||||||
printf(" routes[i].via==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].via))->toIpString().c_str());
|
|
||||||
printf(" routes[i].flags==%.4x\n",(unsigned int)routes[i].flags);
|
|
||||||
printf(" routes[i].metric==%u\n",(unsigned int)routes[i].metric);
|
|
||||||
}
|
|
||||||
printf("staticIpCount==%u\n",staticIpCount);
|
|
||||||
for(unsigned int i=0;i<staticIpCount;++i)
|
|
||||||
printf(" staticIps[i]==%s\n",staticIps[i].toString().c_str());
|
|
||||||
printf("pinnedCount==%u\n",pinnedCount);
|
|
||||||
for(unsigned int i=0;i<pinnedCount;++i) {
|
|
||||||
printf(" pinned[i].zt==%s\n",pinned[i].zt.toString().c_str());
|
|
||||||
printf(" pinned[i].phy==%s\n",pinned[i].phy.toString().c_str());
|
|
||||||
}
|
|
||||||
printf("ruleCount==%u\n",ruleCount);
|
|
||||||
printf("name==%s\n",name);
|
|
||||||
printf("com==%s\n",com.toString().c_str());
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a specialist or mask flags if already present
|
* Add a specialist or mask flags if already present
|
||||||
*
|
*
|
||||||
@@ -453,6 +380,53 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Capability *capability(const uint32_t id) const
|
||||||
|
{
|
||||||
|
for(unsigned int i=0;i<capabilityCount;++i) {
|
||||||
|
if (capabilities[i].id() == id)
|
||||||
|
return &(capabilities[i]);
|
||||||
|
}
|
||||||
|
return (Capability *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Tag *tag(const uint32_t id) const
|
||||||
|
{
|
||||||
|
for(unsigned int i=0;i<tagCount;++i) {
|
||||||
|
if (tags[i].id() == id)
|
||||||
|
return &(tags[i]);
|
||||||
|
}
|
||||||
|
return (Tag *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
inline void dump() const
|
||||||
|
{
|
||||||
|
printf("networkId==%.16llx\n",networkId);
|
||||||
|
printf("timestamp==%llu\n",timestamp);
|
||||||
|
printf("credentialTimeMaxDelta==%llu\n",credentialTimeMaxDelta);
|
||||||
|
printf("revision==%llu\n",revision);
|
||||||
|
printf("issuedTo==%.10llx\n",issuedTo.toInt());
|
||||||
|
printf("multicastLimit==%u\n",multicastLimit);
|
||||||
|
printf("flags=%.8lx\n",(unsigned long)flags);
|
||||||
|
printf("specialistCount==%u\n",specialistCount);
|
||||||
|
for(unsigned int i=0;i<specialistCount;++i)
|
||||||
|
printf(" specialists[%u]==%.16llx\n",i,specialists[i]);
|
||||||
|
printf("routeCount==%u\n",routeCount);
|
||||||
|
for(unsigned int i=0;i<routeCount;++i) {
|
||||||
|
printf(" routes[i].target==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].target))->toString().c_str());
|
||||||
|
printf(" routes[i].via==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].via))->toIpString().c_str());
|
||||||
|
printf(" routes[i].flags==%.4x\n",(unsigned int)routes[i].flags);
|
||||||
|
printf(" routes[i].metric==%u\n",(unsigned int)routes[i].metric);
|
||||||
|
}
|
||||||
|
printf("staticIpCount==%u\n",staticIpCount);
|
||||||
|
for(unsigned int i=0;i<staticIpCount;++i)
|
||||||
|
printf(" staticIps[i]==%s\n",staticIps[i].toString().c_str());
|
||||||
|
printf("ruleCount==%u\n",ruleCount);
|
||||||
|
printf("name==%s\n",name);
|
||||||
|
printf("com==%s\n",com.toString().c_str());
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Network ID that this configuration applies to
|
* Network ID that this configuration applies to
|
||||||
*/
|
*/
|
||||||
@@ -463,6 +437,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max difference between timestamp and tag/capability timestamp
|
||||||
|
*/
|
||||||
|
uint64_t credentialTimeMaxDelta;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller-side revision counter for this configuration
|
* Controller-side revision counter for this configuration
|
||||||
*/
|
*/
|
||||||
@@ -498,16 +477,26 @@ public:
|
|||||||
*/
|
*/
|
||||||
unsigned int staticIpCount;
|
unsigned int staticIpCount;
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of pinned devices (devices with physical address hints)
|
|
||||||
*/
|
|
||||||
unsigned int pinnedCount;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of rule table entries
|
* Number of rule table entries
|
||||||
*/
|
*/
|
||||||
unsigned int ruleCount;
|
unsigned int ruleCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of capabilities
|
||||||
|
*/
|
||||||
|
unsigned int capabilityCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of tags
|
||||||
|
*/
|
||||||
|
unsigned int tagCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of certificates of ownership
|
||||||
|
*/
|
||||||
|
unsigned int certificateOfOwnershipCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialist devices
|
* Specialist devices
|
||||||
*
|
*
|
||||||
@@ -527,21 +516,25 @@ public:
|
|||||||
InetAddress staticIps[ZT_MAX_ZT_ASSIGNED_ADDRESSES];
|
InetAddress staticIps[ZT_MAX_ZT_ASSIGNED_ADDRESSES];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pinned devices with physical address hints
|
* Base network rules
|
||||||
*
|
|
||||||
* These can be used to specify a physical address where a given device
|
|
||||||
* can be reached. It's usually used with network relays (specialists).
|
|
||||||
*/
|
|
||||||
struct {
|
|
||||||
Address zt;
|
|
||||||
InetAddress phy;
|
|
||||||
} pinned[ZT_MAX_NETWORK_PINNED];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rules table
|
|
||||||
*/
|
*/
|
||||||
ZT_VirtualNetworkRule rules[ZT_MAX_NETWORK_RULES];
|
ZT_VirtualNetworkRule rules[ZT_MAX_NETWORK_RULES];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capabilities for this node on this network, in ascending order of capability ID
|
||||||
|
*/
|
||||||
|
Capability capabilities[ZT_MAX_NETWORK_CAPABILITIES];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tags for this node on this network, in ascending order of tag ID
|
||||||
|
*/
|
||||||
|
Tag tags[ZT_MAX_NETWORK_TAGS];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Certificates of ownership for this network member
|
||||||
|
*/
|
||||||
|
CertificateOfOwnership certificatesOfOwnership[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Network type (currently just public or private)
|
* Network type (currently just public or private)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -24,12 +24,12 @@
|
|||||||
#include "Constants.hpp"
|
#include "Constants.hpp"
|
||||||
#include "Dictionary.hpp"
|
#include "Dictionary.hpp"
|
||||||
#include "NetworkConfig.hpp"
|
#include "NetworkConfig.hpp"
|
||||||
|
#include "Revocation.hpp"
|
||||||
|
#include "Address.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
class RuntimeEnvironment;
|
|
||||||
class Identity;
|
class Identity;
|
||||||
class Address;
|
|
||||||
struct InetAddress;
|
struct InetAddress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,45 +38,77 @@ struct InetAddress;
|
|||||||
class NetworkController
|
class NetworkController
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
enum ErrorCode
|
||||||
* Return value of doNetworkConfigRequest
|
|
||||||
*/
|
|
||||||
enum ResultCode
|
|
||||||
{
|
{
|
||||||
NETCONF_QUERY_OK = 0,
|
NC_ERROR_NONE = 0,
|
||||||
NETCONF_QUERY_OBJECT_NOT_FOUND = 1,
|
NC_ERROR_OBJECT_NOT_FOUND = 1,
|
||||||
NETCONF_QUERY_ACCESS_DENIED = 2,
|
NC_ERROR_ACCESS_DENIED = 2,
|
||||||
NETCONF_QUERY_INTERNAL_SERVER_ERROR = 3,
|
NC_ERROR_INTERNAL_SERVER_ERROR = 3
|
||||||
NETCONF_QUERY_IGNORE = 4
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for sender used to send pushes and replies
|
||||||
|
*/
|
||||||
|
class Sender
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Send a configuration to a remote peer
|
||||||
|
*
|
||||||
|
* @param nwid Network ID
|
||||||
|
* @param requestPacketId Request packet ID to send OK(NETWORK_CONFIG_REQUEST) or 0 to send NETWORK_CONFIG (push)
|
||||||
|
* @param destination Destination peer Address
|
||||||
|
* @param nc Network configuration to send
|
||||||
|
* @param sendLegacyFormatConfig If true, send an old-format network config
|
||||||
|
*/
|
||||||
|
virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send revocation to a node
|
||||||
|
*
|
||||||
|
* @param destination Destination node address
|
||||||
|
* @param rev Revocation to send
|
||||||
|
*/
|
||||||
|
virtual void ncSendRevocation(const Address &destination,const Revocation &rev) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a network configuration request error
|
||||||
|
*
|
||||||
|
* @param nwid Network ID
|
||||||
|
* @param requestPacketId Request packet ID or 0 if none
|
||||||
|
* @param destination Destination peer Address
|
||||||
|
* @param errorCode Error code
|
||||||
|
*/
|
||||||
|
virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
NetworkController() {}
|
NetworkController() {}
|
||||||
virtual ~NetworkController() {}
|
virtual ~NetworkController() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a network config request, sending replies if necessary
|
* Called when this is added to a Node to initialize and supply info
|
||||||
*
|
*
|
||||||
* This call is permitted to block, and may be called concurrently from more
|
* @param signingId Identity for signing of network configurations, certs, etc.
|
||||||
* than one thread. Implementations must use locks if needed.
|
* @param sender Sender implementation for sending replies or config pushes
|
||||||
|
*/
|
||||||
|
virtual void init(const Identity &signingId,Sender *sender) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a network configuration request
|
||||||
*
|
*
|
||||||
* On internal server errors, the 'error' field in result can be filled in
|
|
||||||
* to indicate the error.
|
|
||||||
*
|
|
||||||
* @param fromAddr Originating wire address or null address if packet is not direct (or from self)
|
|
||||||
* @param signingId Identity that should be used to sign results -- must include private key
|
|
||||||
* @param identity Originating peer ZeroTier identity
|
|
||||||
* @param nwid 64-bit network ID
|
* @param nwid 64-bit network ID
|
||||||
|
* @param fromAddr Originating wire address or null address if packet is not direct (or from self)
|
||||||
|
* @param requestPacketId Packet ID of request packet or 0 if not initiated by remote request
|
||||||
|
* @param identity ZeroTier identity of originating peer
|
||||||
* @param metaData Meta-data bundled with request (if any)
|
* @param metaData Meta-data bundled with request (if any)
|
||||||
* @param nc NetworkConfig to fill with results
|
|
||||||
* @return Returns NETCONF_QUERY_OK if result 'nc' is valid, or an error code on error
|
* @return Returns NETCONF_QUERY_OK if result 'nc' is valid, or an error code on error
|
||||||
*/
|
*/
|
||||||
virtual NetworkController::ResultCode doNetworkConfigRequest(
|
virtual void request(
|
||||||
const InetAddress &fromAddr,
|
|
||||||
const Identity &signingId,
|
|
||||||
const Identity &identity,
|
|
||||||
uint64_t nwid,
|
uint64_t nwid,
|
||||||
const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &metaData,
|
const InetAddress &fromAddr,
|
||||||
NetworkConfig &nc) = 0;
|
uint64_t requestPacketId,
|
||||||
|
const Identity &identity,
|
||||||
|
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -37,7 +37,6 @@
|
|||||||
#include "Identity.hpp"
|
#include "Identity.hpp"
|
||||||
#include "SelfAwareness.hpp"
|
#include "SelfAwareness.hpp"
|
||||||
#include "Cluster.hpp"
|
#include "Cluster.hpp"
|
||||||
#include "DeferredPackets.hpp"
|
|
||||||
|
|
||||||
const struct sockaddr_storage ZT_SOCKADDR_NULL = {0};
|
const struct sockaddr_storage ZT_SOCKADDR_NULL = {0};
|
||||||
|
|
||||||
@@ -47,45 +46,32 @@ namespace ZeroTier {
|
|||||||
/* Public Node interface (C++, exposed via CAPI bindings) */
|
/* Public Node interface (C++, exposed via CAPI bindings) */
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
|
|
||||||
Node::Node(
|
Node::Node(void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now) :
|
||||||
uint64_t now,
|
|
||||||
void *uptr,
|
|
||||||
ZT_DataStoreGetFunction dataStoreGetFunction,
|
|
||||||
ZT_DataStorePutFunction dataStorePutFunction,
|
|
||||||
ZT_WirePacketSendFunction wirePacketSendFunction,
|
|
||||||
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
|
|
||||||
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
|
|
||||||
ZT_PathCheckFunction pathCheckFunction,
|
|
||||||
ZT_EventCallback eventCallback) :
|
|
||||||
_RR(this),
|
_RR(this),
|
||||||
RR(&_RR),
|
RR(&_RR),
|
||||||
_uPtr(uptr),
|
_uPtr(uptr),
|
||||||
_dataStoreGetFunction(dataStoreGetFunction),
|
|
||||||
_dataStorePutFunction(dataStorePutFunction),
|
|
||||||
_wirePacketSendFunction(wirePacketSendFunction),
|
|
||||||
_virtualNetworkFrameFunction(virtualNetworkFrameFunction),
|
|
||||||
_virtualNetworkConfigFunction(virtualNetworkConfigFunction),
|
|
||||||
_pathCheckFunction(pathCheckFunction),
|
|
||||||
_eventCallback(eventCallback),
|
|
||||||
_networks(),
|
|
||||||
_networks_m(),
|
|
||||||
_prngStreamPtr(0),
|
_prngStreamPtr(0),
|
||||||
_now(now),
|
_now(now),
|
||||||
_lastPingCheck(0),
|
_lastPingCheck(0),
|
||||||
_lastHousekeepingRun(0)
|
_lastHousekeepingRun(0)
|
||||||
{
|
{
|
||||||
|
if (callbacks->version != 0)
|
||||||
|
throw std::runtime_error("callbacks struct version mismatch");
|
||||||
|
memcpy(&_cb,callbacks,sizeof(ZT_Node_Callbacks));
|
||||||
|
|
||||||
_online = false;
|
_online = false;
|
||||||
|
|
||||||
|
memset(_expectingRepliesToBucketPtr,0,sizeof(_expectingRepliesToBucketPtr));
|
||||||
|
memset(_expectingRepliesTo,0,sizeof(_expectingRepliesTo));
|
||||||
|
memset(_lastIdentityVerification,0,sizeof(_lastIdentityVerification));
|
||||||
|
|
||||||
// Use Salsa20 alone as a high-quality non-crypto PRNG
|
// Use Salsa20 alone as a high-quality non-crypto PRNG
|
||||||
{
|
|
||||||
char foo[32];
|
char foo[32];
|
||||||
Utils::getSecureRandom(foo,32);
|
Utils::getSecureRandom(foo,32);
|
||||||
_prng.init(foo,256,foo);
|
_prng.init(foo,256,foo);
|
||||||
memset(_prngStream,0,sizeof(_prngStream));
|
memset(_prngStream,0,sizeof(_prngStream));
|
||||||
_prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream));
|
_prng.crypt12(_prngStream,_prngStream,sizeof(_prngStream));
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::string idtmp(dataStoreGet("identity.secret"));
|
std::string idtmp(dataStoreGet("identity.secret"));
|
||||||
if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
|
if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
|
||||||
TRACE("identity.secret not found, generating...");
|
TRACE("identity.secret not found, generating...");
|
||||||
@@ -101,16 +87,13 @@ Node::Node(
|
|||||||
if (!dataStorePut("identity.public",RR->publicIdentityStr,false))
|
if (!dataStorePut("identity.public",RR->publicIdentityStr,false))
|
||||||
throw std::runtime_error("unable to write identity.public");
|
throw std::runtime_error("unable to write identity.public");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
RR->sw = new Switch(RR);
|
RR->sw = new Switch(RR);
|
||||||
RR->mc = new Multicaster(RR);
|
RR->mc = new Multicaster(RR);
|
||||||
RR->topology = new Topology(RR);
|
RR->topology = new Topology(RR);
|
||||||
RR->sa = new SelfAwareness(RR);
|
RR->sa = new SelfAwareness(RR);
|
||||||
RR->dp = new DeferredPackets(RR);
|
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
delete RR->dp;
|
|
||||||
delete RR->sa;
|
delete RR->sa;
|
||||||
delete RR->topology;
|
delete RR->topology;
|
||||||
delete RR->mc;
|
delete RR->mc;
|
||||||
@@ -127,12 +110,11 @@ Node::~Node()
|
|||||||
|
|
||||||
_networks.clear(); // ensure that networks are destroyed before shutdow
|
_networks.clear(); // ensure that networks are destroyed before shutdow
|
||||||
|
|
||||||
RR->dpEnabled = 0;
|
|
||||||
delete RR->dp;
|
|
||||||
delete RR->sa;
|
delete RR->sa;
|
||||||
delete RR->topology;
|
delete RR->topology;
|
||||||
delete RR->mc;
|
delete RR->mc;
|
||||||
delete RR->sw;
|
delete RR->sw;
|
||||||
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
delete RR->cluster;
|
delete RR->cluster;
|
||||||
#endif
|
#endif
|
||||||
@@ -170,15 +152,16 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
|
|||||||
} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
|
} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Closure used to ping upstream and active/online peers
|
||||||
class _PingPeersThatNeedPing
|
class _PingPeersThatNeedPing
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
_PingPeersThatNeedPing(const RuntimeEnvironment *renv,uint64_t now,const std::vector<NetworkConfig::Relay> &relays) :
|
_PingPeersThatNeedPing(const RuntimeEnvironment *renv,Hashtable< Address,std::vector<InetAddress> > &upstreamsToContact,uint64_t now) :
|
||||||
lastReceiveFromUpstream(0),
|
lastReceiveFromUpstream(0),
|
||||||
RR(renv),
|
RR(renv),
|
||||||
|
_upstreamsToContact(upstreamsToContact),
|
||||||
_now(now),
|
_now(now),
|
||||||
_relays(relays),
|
_bestCurrentUpstream(RR->topology->getUpstreamPeer())
|
||||||
_world(RR->topology->world())
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,89 +169,52 @@ public:
|
|||||||
|
|
||||||
inline void operator()(Topology &t,const SharedPtr<Peer> &p)
|
inline void operator()(Topology &t,const SharedPtr<Peer> &p)
|
||||||
{
|
{
|
||||||
bool upstream = false;
|
const std::vector<InetAddress> *const upstreamStableEndpoints = _upstreamsToContact.get(p->address());
|
||||||
InetAddress stableEndpoint4,stableEndpoint6;
|
if (upstreamStableEndpoints) {
|
||||||
|
bool contacted = false;
|
||||||
|
|
||||||
// If this is a world root, pick (if possible) both an IPv4 and an IPv6 stable endpoint to use if link isn't currently alive.
|
// Upstreams must be pinged constantly over both IPv4 and IPv6 to allow
|
||||||
for(std::vector<World::Root>::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) {
|
// them to perform three way handshake introductions for both stacks.
|
||||||
if (r->identity == p->identity()) {
|
|
||||||
upstream = true;
|
if (!p->doPingAndKeepalive(_now,AF_INET)) {
|
||||||
for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)r->stableEndpoints.size();++k) {
|
for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)upstreamStableEndpoints->size();++k) {
|
||||||
const InetAddress &addr = r->stableEndpoints[ptr++ % r->stableEndpoints.size()];
|
const InetAddress &addr = (*upstreamStableEndpoints)[ptr++ % upstreamStableEndpoints->size()];
|
||||||
if (!stableEndpoint4) {
|
if (addr.ss_family == AF_INET) {
|
||||||
if (addr.ss_family == AF_INET)
|
p->sendHELLO(InetAddress(),addr,_now,0);
|
||||||
stableEndpoint4 = addr;
|
contacted = true;
|
||||||
}
|
|
||||||
if (!stableEndpoint6) {
|
|
||||||
if (addr.ss_family == AF_INET6)
|
|
||||||
stableEndpoint6 = addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else contacted = true;
|
||||||
if (!upstream) {
|
if (!p->doPingAndKeepalive(_now,AF_INET6)) {
|
||||||
// If I am a root server, only ping other root servers -- roots don't ping "down"
|
for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)upstreamStableEndpoints->size();++k) {
|
||||||
// since that would just be a waste of bandwidth and could potentially cause route
|
const InetAddress &addr = (*upstreamStableEndpoints)[ptr++ % upstreamStableEndpoints->size()];
|
||||||
// flapping in Cluster mode.
|
if (addr.ss_family == AF_INET6) {
|
||||||
if (RR->topology->amRoot())
|
p->sendHELLO(InetAddress(),addr,_now,0);
|
||||||
return;
|
contacted = true;
|
||||||
|
|
||||||
// Check for network preferred relays, also considered 'upstream' and thus always
|
|
||||||
// pinged to keep links up. If they have stable addresses we will try them there.
|
|
||||||
for(std::vector<NetworkConfig::Relay>::const_iterator r(_relays.begin());r!=_relays.end();++r) {
|
|
||||||
if (r->address == p->address()) {
|
|
||||||
stableEndpoint4 = r->phy4;
|
|
||||||
stableEndpoint6 = r->phy6;
|
|
||||||
upstream = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else contacted = true;
|
||||||
|
|
||||||
if (upstream) {
|
if ((!contacted)&&(_bestCurrentUpstream)) {
|
||||||
// "Upstream" devices are roots and relays and get special treatment -- they stay alive
|
const SharedPtr<Path> up(_bestCurrentUpstream->getBestPath(_now,true));
|
||||||
// forever and we try to keep (if available) both IPv4 and IPv6 channels open to them.
|
if (up)
|
||||||
bool needToContactIndirect = true;
|
p->sendHELLO(up->localAddress(),up->address(),_now,up->nextOutgoingCounter());
|
||||||
if (p->doPingAndKeepalive(_now,AF_INET)) {
|
|
||||||
needToContactIndirect = false;
|
|
||||||
} else {
|
|
||||||
if (stableEndpoint4) {
|
|
||||||
needToContactIndirect = false;
|
|
||||||
p->sendHELLO(InetAddress(),stableEndpoint4,_now);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (p->doPingAndKeepalive(_now,AF_INET6)) {
|
|
||||||
needToContactIndirect = false;
|
|
||||||
} else {
|
|
||||||
if (stableEndpoint6) {
|
|
||||||
needToContactIndirect = false;
|
|
||||||
p->sendHELLO(InetAddress(),stableEndpoint6,_now);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needToContactIndirect) {
|
|
||||||
// If this is an upstream and we have no stable endpoint for either IPv4 or IPv6,
|
|
||||||
// send a NOP indirectly if possible to see if we can get to this peer in any
|
|
||||||
// way whatsoever. This will e.g. find network preferred relays that lack
|
|
||||||
// stable endpoints by using root servers.
|
|
||||||
Packet outp(p->address(),RR->identity.address(),Packet::VERB_NOP);
|
|
||||||
RR->sw->send(outp,true,0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);
|
lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);
|
||||||
} else if (p->activelyTransferringFrames(_now)) {
|
_upstreamsToContact.erase(p->address()); // erase from upstreams to contact so that we can WHOIS those that remain
|
||||||
// Normal nodes get their preferred link kept alive if the node has generated frame traffic recently
|
} else if (p->isActive(_now)) {
|
||||||
p->doPingAndKeepalive(_now,0);
|
p->doPingAndKeepalive(_now,-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const RuntimeEnvironment *RR;
|
const RuntimeEnvironment *RR;
|
||||||
uint64_t _now;
|
Hashtable< Address,std::vector<InetAddress> > &_upstreamsToContact;
|
||||||
const std::vector<NetworkConfig::Relay> &_relays;
|
const uint64_t _now;
|
||||||
World _world;
|
const SharedPtr<Peer> _bestCurrentUpstream;
|
||||||
};
|
};
|
||||||
|
|
||||||
ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
|
ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
|
||||||
@@ -282,30 +228,32 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
|
|||||||
try {
|
try {
|
||||||
_lastPingCheck = now;
|
_lastPingCheck = now;
|
||||||
|
|
||||||
// Get relays and networks that need config without leaving the mutex locked
|
// Get networks that need config without leaving mutex locked
|
||||||
std::vector< NetworkConfig::Relay > networkRelays;
|
|
||||||
std::vector< SharedPtr<Network> > needConfig;
|
std::vector< SharedPtr<Network> > needConfig;
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_networks_m);
|
Mutex::Lock _l(_networks_m);
|
||||||
for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
|
for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
|
||||||
if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig())) {
|
if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig()))
|
||||||
needConfig.push_back(n->second);
|
needConfig.push_back(n->second);
|
||||||
}
|
n->second->sendUpdatesToMembers();
|
||||||
if (n->second->hasConfig()) {
|
|
||||||
std::vector<NetworkConfig::Relay> r(n->second->config().relays());
|
|
||||||
networkRelays.insert(networkRelays.end(),r.begin(),r.end());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Request updated configuration for networks that need it
|
|
||||||
for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n)
|
for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n)
|
||||||
(*n)->requestConfiguration();
|
(*n)->requestConfiguration();
|
||||||
|
|
||||||
// Do pings and keepalives
|
// Do pings and keepalives
|
||||||
_PingPeersThatNeedPing pfunc(RR,now,networkRelays);
|
Hashtable< Address,std::vector<InetAddress> > upstreamsToContact;
|
||||||
|
RR->topology->getUpstreamsToContact(upstreamsToContact);
|
||||||
|
_PingPeersThatNeedPing pfunc(RR,upstreamsToContact,now);
|
||||||
RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc);
|
RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc);
|
||||||
|
|
||||||
|
// Run WHOIS to create Peer for any upstreams we could not contact (including pending moon seeds)
|
||||||
|
Hashtable< Address,std::vector<InetAddress> >::Iterator i(upstreamsToContact);
|
||||||
|
Address *upstreamAddress = (Address *)0;
|
||||||
|
std::vector<InetAddress> *upstreamStableEndpoints = (std::vector<InetAddress> *)0;
|
||||||
|
while (i.next(upstreamAddress,upstreamStableEndpoints))
|
||||||
|
RR->sw->requestWhois(*upstreamAddress);
|
||||||
|
|
||||||
// Update online status, post status change as event
|
// Update online status, post status change as event
|
||||||
const bool oldOnline = _online;
|
const bool oldOnline = _online;
|
||||||
_online = (((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amRoot()));
|
_online = (((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amRoot()));
|
||||||
@@ -394,6 +342,18 @@ ZT_ResultCode Node::multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,u
|
|||||||
} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
|
} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZT_ResultCode Node::orbit(uint64_t moonWorldId,uint64_t moonSeed)
|
||||||
|
{
|
||||||
|
RR->topology->addMoon(moonWorldId,Address(moonSeed));
|
||||||
|
return ZT_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZT_ResultCode Node::deorbit(uint64_t moonWorldId)
|
||||||
|
{
|
||||||
|
RR->topology->removeMoon(moonWorldId);
|
||||||
|
return ZT_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t Node::address() const
|
uint64_t Node::address() const
|
||||||
{
|
{
|
||||||
return RR->identity.address().toInt();
|
return RR->identity.address().toInt();
|
||||||
@@ -402,8 +362,6 @@ uint64_t Node::address() const
|
|||||||
void Node::status(ZT_NodeStatus *status) const
|
void Node::status(ZT_NodeStatus *status) const
|
||||||
{
|
{
|
||||||
status->address = RR->identity.address().toInt();
|
status->address = RR->identity.address().toInt();
|
||||||
status->worldId = RR->topology->worldId();
|
|
||||||
status->worldTimestamp = RR->topology->worldTimestamp();
|
|
||||||
status->publicIdentity = RR->publicIdentityStr.c_str();
|
status->publicIdentity = RR->publicIdentityStr.c_str();
|
||||||
status->secretIdentity = RR->secretIdentityStr.c_str();
|
status->secretIdentity = RR->secretIdentityStr.c_str();
|
||||||
status->online = _online ? 1 : 0;
|
status->online = _online ? 1 : 0;
|
||||||
@@ -424,8 +382,6 @@ ZT_PeerList *Node::peers() const
|
|||||||
for(std::vector< std::pair< Address,SharedPtr<Peer> > >::iterator pi(peers.begin());pi!=peers.end();++pi) {
|
for(std::vector< std::pair< Address,SharedPtr<Peer> > >::iterator pi(peers.begin());pi!=peers.end();++pi) {
|
||||||
ZT_Peer *p = &(pl->peers[pl->peerCount++]);
|
ZT_Peer *p = &(pl->peers[pl->peerCount++]);
|
||||||
p->address = pi->second->address().toInt();
|
p->address = pi->second->address().toInt();
|
||||||
p->lastUnicastFrame = pi->second->lastUnicastFrame();
|
|
||||||
p->lastMulticastFrame = pi->second->lastMulticastFrame();
|
|
||||||
if (pi->second->remoteVersionKnown()) {
|
if (pi->second->remoteVersionKnown()) {
|
||||||
p->versionMajor = pi->second->remoteVersionMajor();
|
p->versionMajor = pi->second->remoteVersionMajor();
|
||||||
p->versionMinor = pi->second->remoteVersionMinor();
|
p->versionMinor = pi->second->remoteVersionMinor();
|
||||||
@@ -436,18 +392,19 @@ ZT_PeerList *Node::peers() const
|
|||||||
p->versionRev = -1;
|
p->versionRev = -1;
|
||||||
}
|
}
|
||||||
p->latency = pi->second->latency();
|
p->latency = pi->second->latency();
|
||||||
p->role = RR->topology->isRoot(pi->second->identity()) ? ZT_PEER_ROLE_ROOT : ZT_PEER_ROLE_LEAF;
|
p->role = RR->topology->role(pi->second->identity().address());
|
||||||
|
|
||||||
std::vector<Path> paths(pi->second->paths());
|
std::vector< std::pair< SharedPtr<Path>,bool > > paths(pi->second->paths(_now));
|
||||||
Path *bestPath = pi->second->getBestPath(_now);
|
SharedPtr<Path> bestp(pi->second->getBestPath(_now,false));
|
||||||
p->pathCount = 0;
|
p->pathCount = 0;
|
||||||
for(std::vector<Path>::iterator path(paths.begin());path!=paths.end();++path) {
|
for(std::vector< std::pair< SharedPtr<Path>,bool > >::iterator path(paths.begin());path!=paths.end();++path) {
|
||||||
memcpy(&(p->paths[p->pathCount].address),&(path->address()),sizeof(struct sockaddr_storage));
|
memcpy(&(p->paths[p->pathCount].address),&(path->first->address()),sizeof(struct sockaddr_storage));
|
||||||
p->paths[p->pathCount].lastSend = path->lastSend();
|
p->paths[p->pathCount].lastSend = path->first->lastOut();
|
||||||
p->paths[p->pathCount].lastReceive = path->lastReceived();
|
p->paths[p->pathCount].lastReceive = path->first->lastIn();
|
||||||
p->paths[p->pathCount].active = path->active(_now) ? 1 : 0;
|
p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust(path->first->address());
|
||||||
p->paths[p->pathCount].preferred = ((bestPath)&&(*path == *bestPath)) ? 1 : 0;
|
p->paths[p->pathCount].linkQuality = (int)path->first->linkQuality();
|
||||||
p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust(path->address());
|
p->paths[p->pathCount].expired = path->second;
|
||||||
|
p->paths[p->pathCount].preferred = (path->first == bestp) ? 1 : 0;
|
||||||
++p->pathCount;
|
++p->pathCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -508,9 +465,25 @@ void Node::clearLocalInterfaceAddresses()
|
|||||||
_directPaths.clear();
|
_directPaths.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Node::sendUserMessage(uint64_t dest,uint64_t typeId,const void *data,unsigned int len)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (RR->identity.address().toInt() != dest) {
|
||||||
|
Packet outp(Address(dest),RR->identity.address(),Packet::VERB_USER_MESSAGE);
|
||||||
|
outp.append(typeId);
|
||||||
|
outp.append(data,len);
|
||||||
|
outp.compress();
|
||||||
|
RR->sw->send(outp,true);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} catch ( ... ) {}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void Node::setNetconfMaster(void *networkControllerInstance)
|
void Node::setNetconfMaster(void *networkControllerInstance)
|
||||||
{
|
{
|
||||||
RR->localNetworkController = reinterpret_cast<NetworkController *>(networkControllerInstance);
|
RR->localNetworkController = reinterpret_cast<NetworkController *>(networkControllerInstance);
|
||||||
|
RR->localNetworkController->init(RR->identity,this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
|
ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
|
||||||
@@ -543,7 +516,7 @@ ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)
|
|||||||
for(unsigned int a=0;a<test->hops[0].breadth;++a) {
|
for(unsigned int a=0;a<test->hops[0].breadth;++a) {
|
||||||
outp.newInitializationVector();
|
outp.newInitializationVector();
|
||||||
outp.setDestination(Address(test->hops[0].addresses[a]));
|
outp.setDestination(Address(test->hops[0].addresses[a]));
|
||||||
RR->sw->send(outp,true,0);
|
RR->sw->send(outp,true);
|
||||||
}
|
}
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
return ZT_RESULT_FATAL_ERROR_INTERNAL; // probably indicates FIFO too big for packet
|
return ZT_RESULT_FATAL_ERROR_INTERNAL; // probably indicates FIFO too big for packet
|
||||||
@@ -639,18 +612,6 @@ void Node::clusterStatus(ZT_ClusterStatus *cs)
|
|||||||
memset(cs,0,sizeof(ZT_ClusterStatus));
|
memset(cs,0,sizeof(ZT_ClusterStatus));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node::backgroundThreadMain()
|
|
||||||
{
|
|
||||||
++RR->dpEnabled;
|
|
||||||
for(;;) {
|
|
||||||
try {
|
|
||||||
if (RR->dp->process() < 0)
|
|
||||||
break;
|
|
||||||
} catch ( ... ) {} // sanity check -- should not throw
|
|
||||||
}
|
|
||||||
--RR->dpEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
/* Node methods used only within node/ */
|
/* Node methods used only within node/ */
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
@@ -661,7 +622,7 @@ std::string Node::dataStoreGet(const char *name)
|
|||||||
std::string r;
|
std::string r;
|
||||||
unsigned long olen = 0;
|
unsigned long olen = 0;
|
||||||
do {
|
do {
|
||||||
long n = _dataStoreGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen);
|
long n = _cb.dataStoreGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen);
|
||||||
if (n <= 0)
|
if (n <= 0)
|
||||||
return std::string();
|
return std::string();
|
||||||
r.append(buf,n);
|
r.append(buf,n);
|
||||||
@@ -669,11 +630,14 @@ std::string Node::dataStoreGet(const char *name)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Node::shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress)
|
bool Node::shouldUsePathForZeroTierTraffic(const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress)
|
||||||
{
|
{
|
||||||
if (!Path::isAddressValidForPath(remoteAddress))
|
if (!Path::isAddressValidForPath(remoteAddress))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (RR->topology->isProhibitedEndpoint(ztaddr,remoteAddress))
|
||||||
|
return false;
|
||||||
|
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_networks_m);
|
Mutex::Lock _l(_networks_m);
|
||||||
for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
|
for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
|
||||||
@@ -686,9 +650,7 @@ bool Node::shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pathCheckFunction)
|
return ( (_cb.pathCheckFunction) ? (_cb.pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,ztaddr.toInt(),reinterpret_cast<const struct sockaddr_storage *>(&localAddress),reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0) : true);
|
||||||
return (_pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,reinterpret_cast<const struct sockaddr_storage *>(&localAddress),reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0);
|
|
||||||
else return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ZT_TRACE
|
#ifdef ZT_TRACE
|
||||||
@@ -726,9 +688,9 @@ void Node::postTrace(const char *module,unsigned int line,const char *fmt,...)
|
|||||||
|
|
||||||
uint64_t Node::prng()
|
uint64_t Node::prng()
|
||||||
{
|
{
|
||||||
unsigned int p = (++_prngStreamPtr % (sizeof(_prngStream) / sizeof(uint64_t)));
|
unsigned int p = (++_prngStreamPtr % ZT_NODE_PRNG_BUF_SIZE);
|
||||||
if (!p)
|
if (!p)
|
||||||
_prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream));
|
_prng.crypt12(_prngStream,_prngStream,sizeof(_prngStream));
|
||||||
return _prngStream[p];
|
return _prngStream[p];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -751,6 +713,120 @@ void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_
|
|||||||
RR->topology->setTrustedPaths(reinterpret_cast<const InetAddress *>(networks),ids,count);
|
RR->topology->setTrustedPaths(reinterpret_cast<const InetAddress *>(networks),ids,count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
World Node::planet() const
|
||||||
|
{
|
||||||
|
return RR->topology->planet();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<World> Node::moons() const
|
||||||
|
{
|
||||||
|
return RR->topology->moons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig)
|
||||||
|
{
|
||||||
|
if (destination == RR->identity.address()) {
|
||||||
|
SharedPtr<Network> n(network(nwid));
|
||||||
|
if (!n) return;
|
||||||
|
n->setConfiguration(nc,true);
|
||||||
|
} else {
|
||||||
|
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
||||||
|
try {
|
||||||
|
if (nc.toDictionary(*dconf,sendLegacyFormatConfig)) {
|
||||||
|
uint64_t configUpdateId = prng();
|
||||||
|
if (!configUpdateId) ++configUpdateId;
|
||||||
|
|
||||||
|
const unsigned int totalSize = dconf->sizeBytes();
|
||||||
|
unsigned int chunkIndex = 0;
|
||||||
|
while (chunkIndex < totalSize) {
|
||||||
|
const unsigned int chunkLen = std::min(totalSize - chunkIndex,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - (ZT_PACKET_IDX_PAYLOAD + 256)));
|
||||||
|
Packet outp(destination,RR->identity.address(),(requestPacketId) ? Packet::VERB_OK : Packet::VERB_NETWORK_CONFIG);
|
||||||
|
if (requestPacketId) {
|
||||||
|
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
|
||||||
|
outp.append(requestPacketId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned int sigStart = outp.size();
|
||||||
|
outp.append(nwid);
|
||||||
|
outp.append((uint16_t)chunkLen);
|
||||||
|
outp.append((const void *)(dconf->data() + chunkIndex),chunkLen);
|
||||||
|
|
||||||
|
outp.append((uint8_t)0); // no flags
|
||||||
|
outp.append((uint64_t)configUpdateId);
|
||||||
|
outp.append((uint32_t)totalSize);
|
||||||
|
outp.append((uint32_t)chunkIndex);
|
||||||
|
|
||||||
|
C25519::Signature sig(RR->identity.sign(reinterpret_cast<const uint8_t *>(outp.data()) + sigStart,outp.size() - sigStart));
|
||||||
|
outp.append((uint8_t)1);
|
||||||
|
outp.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
|
||||||
|
outp.append(sig.data,ZT_C25519_SIGNATURE_LEN);
|
||||||
|
|
||||||
|
outp.compress();
|
||||||
|
RR->sw->send(outp,true);
|
||||||
|
chunkIndex += chunkLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete dconf;
|
||||||
|
} catch ( ... ) {
|
||||||
|
delete dconf;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Node::ncSendRevocation(const Address &destination,const Revocation &rev)
|
||||||
|
{
|
||||||
|
if (destination == RR->identity.address()) {
|
||||||
|
SharedPtr<Network> n(network(rev.networkId()));
|
||||||
|
if (!n) return;
|
||||||
|
n->addCredential(RR->identity.address(),rev);
|
||||||
|
} else {
|
||||||
|
Packet outp(destination,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
|
||||||
|
outp.append((uint8_t)0x00);
|
||||||
|
outp.append((uint16_t)0);
|
||||||
|
outp.append((uint16_t)0);
|
||||||
|
outp.append((uint16_t)1);
|
||||||
|
rev.serialize(outp);
|
||||||
|
outp.append((uint16_t)0);
|
||||||
|
RR->sw->send(outp,true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode)
|
||||||
|
{
|
||||||
|
if (destination == RR->identity.address()) {
|
||||||
|
SharedPtr<Network> n(network(nwid));
|
||||||
|
if (!n) return;
|
||||||
|
switch(errorCode) {
|
||||||
|
case NetworkController::NC_ERROR_OBJECT_NOT_FOUND:
|
||||||
|
case NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR:
|
||||||
|
n->setNotFound();
|
||||||
|
break;
|
||||||
|
case NetworkController::NC_ERROR_ACCESS_DENIED:
|
||||||
|
n->setAccessDenied();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
} else if (requestPacketId) {
|
||||||
|
Packet outp(destination,RR->identity.address(),Packet::VERB_ERROR);
|
||||||
|
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
|
||||||
|
outp.append(requestPacketId);
|
||||||
|
switch(errorCode) {
|
||||||
|
//case NetworkController::NC_ERROR_OBJECT_NOT_FOUND:
|
||||||
|
//case NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR:
|
||||||
|
default:
|
||||||
|
outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND);
|
||||||
|
break;
|
||||||
|
case NetworkController::NC_ERROR_ACCESS_DENIED:
|
||||||
|
outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
outp.append(nwid);
|
||||||
|
RR->sw->send(outp,true);
|
||||||
|
} // else we can't send an ERROR() in response to nothing, so discard
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
@@ -759,21 +835,11 @@ void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
enum ZT_ResultCode ZT_Node_new(
|
enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now)
|
||||||
ZT_Node **node,
|
|
||||||
void *uptr,
|
|
||||||
uint64_t now,
|
|
||||||
ZT_DataStoreGetFunction dataStoreGetFunction,
|
|
||||||
ZT_DataStorePutFunction dataStorePutFunction,
|
|
||||||
ZT_WirePacketSendFunction wirePacketSendFunction,
|
|
||||||
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
|
|
||||||
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
|
|
||||||
ZT_PathCheckFunction pathCheckFunction,
|
|
||||||
ZT_EventCallback eventCallback)
|
|
||||||
{
|
{
|
||||||
*node = (ZT_Node *)0;
|
*node = (ZT_Node *)0;
|
||||||
try {
|
try {
|
||||||
*node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,pathCheckFunction,eventCallback));
|
*node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(uptr,callbacks,now));
|
||||||
return ZT_RESULT_OK;
|
return ZT_RESULT_OK;
|
||||||
} catch (std::bad_alloc &exc) {
|
} catch (std::bad_alloc &exc) {
|
||||||
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
|
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
|
||||||
@@ -885,6 +951,24 @@ enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,uint64_t moonWorldId,uint64_t moonSeed)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return reinterpret_cast<ZeroTier::Node *>(node)->orbit(moonWorldId,moonSeed);
|
||||||
|
} catch ( ... ) {
|
||||||
|
return ZT_RESULT_FATAL_ERROR_INTERNAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,uint64_t moonWorldId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return reinterpret_cast<ZeroTier::Node *>(node)->deorbit(moonWorldId);
|
||||||
|
} catch ( ... ) {
|
||||||
|
return ZT_RESULT_FATAL_ERROR_INTERNAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t ZT_Node_address(ZT_Node *node)
|
uint64_t ZT_Node_address(ZT_Node *node)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<ZeroTier::Node *>(node)->address();
|
return reinterpret_cast<ZeroTier::Node *>(node)->address();
|
||||||
@@ -947,6 +1031,15 @@ void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node)
|
|||||||
} catch ( ... ) {}
|
} catch ( ... ) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ZT_Node_sendUserMessage(ZT_Node *node,uint64_t dest,uint64_t typeId,const void *data,unsigned int len)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return reinterpret_cast<ZeroTier::Node *>(node)->sendUserMessage(dest,typeId,data,len);
|
||||||
|
} catch ( ... ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance)
|
void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -1027,13 +1120,6 @@ void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networ
|
|||||||
} catch ( ... ) {}
|
} catch ( ... ) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZT_Node_backgroundThreadMain(ZT_Node *node)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
reinterpret_cast<ZeroTier::Node *>(node)->backgroundThreadMain();
|
|
||||||
} catch ( ... ) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ZT_version(int *major,int *minor,int *revision)
|
void ZT_version(int *major,int *minor,int *revision)
|
||||||
{
|
{
|
||||||
if (major) *major = ZEROTIER_ONE_VERSION_MAJOR;
|
if (major) *major = ZEROTIER_ONE_VERSION_MAJOR;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "Constants.hpp"
|
#include "Constants.hpp"
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
#include "Network.hpp"
|
#include "Network.hpp"
|
||||||
#include "Path.hpp"
|
#include "Path.hpp"
|
||||||
#include "Salsa20.hpp"
|
#include "Salsa20.hpp"
|
||||||
|
#include "NetworkController.hpp"
|
||||||
|
|
||||||
#undef TRACE
|
#undef TRACE
|
||||||
#ifdef ZT_TRACE
|
#ifdef ZT_TRACE
|
||||||
@@ -44,28 +46,33 @@
|
|||||||
#define TRACE(f,...) {}
|
#define TRACE(f,...) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Bit mask for "expecting reply" hash
|
||||||
|
#define ZT_EXPECTING_REPLIES_BUCKET_MASK1 255
|
||||||
|
#define ZT_EXPECTING_REPLIES_BUCKET_MASK2 31
|
||||||
|
|
||||||
|
// Size of PRNG stream buffer
|
||||||
|
#define ZT_NODE_PRNG_BUF_SIZE 64
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
class World;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of Node object as defined in CAPI
|
* Implementation of Node object as defined in CAPI
|
||||||
*
|
*
|
||||||
* The pointer returned by ZT_Node_new() is an instance of this class.
|
* The pointer returned by ZT_Node_new() is an instance of this class.
|
||||||
*/
|
*/
|
||||||
class Node
|
class Node : public NetworkController::Sender
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Node(
|
Node(void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now);
|
||||||
uint64_t now,
|
virtual ~Node();
|
||||||
void *uptr,
|
|
||||||
ZT_DataStoreGetFunction dataStoreGetFunction,
|
|
||||||
ZT_DataStorePutFunction dataStorePutFunction,
|
|
||||||
ZT_WirePacketSendFunction wirePacketSendFunction,
|
|
||||||
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
|
|
||||||
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
|
|
||||||
ZT_PathCheckFunction pathCheckFunction,
|
|
||||||
ZT_EventCallback eventCallback);
|
|
||||||
|
|
||||||
~Node();
|
// Get rid of alignment warnings on 32-bit Windows and possibly improve performance
|
||||||
|
#ifdef __WINDOWS__
|
||||||
|
void * operator new(size_t i) { return _mm_malloc(i,16); }
|
||||||
|
void operator delete(void* p) { _mm_free(p); }
|
||||||
|
#endif
|
||||||
|
|
||||||
// Public API Functions ----------------------------------------------------
|
// Public API Functions ----------------------------------------------------
|
||||||
|
|
||||||
@@ -91,6 +98,8 @@ public:
|
|||||||
ZT_ResultCode leave(uint64_t nwid,void **uptr);
|
ZT_ResultCode leave(uint64_t nwid,void **uptr);
|
||||||
ZT_ResultCode multicastSubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
|
ZT_ResultCode multicastSubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
|
||||||
ZT_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
|
ZT_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
|
||||||
|
ZT_ResultCode orbit(uint64_t moonWorldId,uint64_t moonSeed);
|
||||||
|
ZT_ResultCode deorbit(uint64_t moonWorldId);
|
||||||
uint64_t address() const;
|
uint64_t address() const;
|
||||||
void status(ZT_NodeStatus *status) const;
|
void status(ZT_NodeStatus *status) const;
|
||||||
ZT_PeerList *peers() const;
|
ZT_PeerList *peers() const;
|
||||||
@@ -99,6 +108,7 @@ public:
|
|||||||
void freeQueryResult(void *qr);
|
void freeQueryResult(void *qr);
|
||||||
int addLocalInterfaceAddress(const struct sockaddr_storage *addr);
|
int addLocalInterfaceAddress(const struct sockaddr_storage *addr);
|
||||||
void clearLocalInterfaceAddresses();
|
void clearLocalInterfaceAddresses();
|
||||||
|
int sendUserMessage(uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
|
||||||
void setNetconfMaster(void *networkControllerInstance);
|
void setNetconfMaster(void *networkControllerInstance);
|
||||||
ZT_ResultCode circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *));
|
ZT_ResultCode circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *));
|
||||||
void circuitTestEnd(ZT_CircuitTest *test);
|
void circuitTestEnd(ZT_CircuitTest *test);
|
||||||
@@ -117,36 +127,14 @@ public:
|
|||||||
void clusterRemoveMember(unsigned int memberId);
|
void clusterRemoveMember(unsigned int memberId);
|
||||||
void clusterHandleIncomingMessage(const void *msg,unsigned int len);
|
void clusterHandleIncomingMessage(const void *msg,unsigned int len);
|
||||||
void clusterStatus(ZT_ClusterStatus *cs);
|
void clusterStatus(ZT_ClusterStatus *cs);
|
||||||
void backgroundThreadMain();
|
|
||||||
|
|
||||||
// Internal functions ------------------------------------------------------
|
// Internal functions ------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience threadMain() for easy background thread launch
|
|
||||||
*
|
|
||||||
* This allows background threads to be launched with Thread::start
|
|
||||||
* that will run against this node.
|
|
||||||
*/
|
|
||||||
inline void threadMain() throw() { this->backgroundThreadMain(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Time as of last call to run()
|
|
||||||
*/
|
|
||||||
inline uint64_t now() const throw() { return _now; }
|
inline uint64_t now() const throw() { return _now; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Enqueue a ZeroTier message to be sent
|
|
||||||
*
|
|
||||||
* @param localAddress Local address
|
|
||||||
* @param addr Destination address
|
|
||||||
* @param data Packet data
|
|
||||||
* @param len Packet length
|
|
||||||
* @param ttl Desired TTL (default: 0 for unchanged/default TTL)
|
|
||||||
* @return True if packet appears to have been sent
|
|
||||||
*/
|
|
||||||
inline bool putPacket(const InetAddress &localAddress,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
|
inline bool putPacket(const InetAddress &localAddress,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
|
||||||
{
|
{
|
||||||
return (_wirePacketSendFunction(
|
return (_cb.wirePacketSendFunction(
|
||||||
reinterpret_cast<ZT_Node *>(this),
|
reinterpret_cast<ZT_Node *>(this),
|
||||||
_uPtr,
|
_uPtr,
|
||||||
reinterpret_cast<const struct sockaddr_storage *>(&localAddress),
|
reinterpret_cast<const struct sockaddr_storage *>(&localAddress),
|
||||||
@@ -156,21 +144,9 @@ public:
|
|||||||
ttl) == 0);
|
ttl) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Enqueue a frame to be injected into a tap device (port)
|
|
||||||
*
|
|
||||||
* @param nwid Network ID
|
|
||||||
* @param nuptr Network user ptr
|
|
||||||
* @param source Source MAC
|
|
||||||
* @param dest Destination MAC
|
|
||||||
* @param etherType 16-bit ethernet type
|
|
||||||
* @param vlanId VLAN ID or 0 if none
|
|
||||||
* @param data Frame data
|
|
||||||
* @param len Frame length
|
|
||||||
*/
|
|
||||||
inline void putFrame(uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
|
inline void putFrame(uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
|
||||||
{
|
{
|
||||||
_virtualNetworkFrameFunction(
|
_cb.virtualNetworkFrameFunction(
|
||||||
reinterpret_cast<ZT_Node *>(this),
|
reinterpret_cast<ZT_Node *>(this),
|
||||||
_uPtr,
|
_uPtr,
|
||||||
nwid,
|
nwid,
|
||||||
@@ -183,13 +159,6 @@ public:
|
|||||||
len);
|
len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param localAddress Local address
|
|
||||||
* @param remoteAddress Remote address
|
|
||||||
* @return True if path should be used
|
|
||||||
*/
|
|
||||||
bool shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress);
|
|
||||||
|
|
||||||
inline SharedPtr<Network> network(uint64_t nwid) const
|
inline SharedPtr<Network> network(uint64_t nwid) const
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_networks_m);
|
Mutex::Lock _l(_networks_m);
|
||||||
@@ -216,37 +185,20 @@ public:
|
|||||||
return nw;
|
return nw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Potential direct paths to me a.k.a. local interface addresses
|
|
||||||
*/
|
|
||||||
inline std::vector<InetAddress> directPaths() const
|
inline std::vector<InetAddress> directPaths() const
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_directPaths_m);
|
Mutex::Lock _l(_directPaths_m);
|
||||||
return _directPaths;
|
return _directPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool dataStorePut(const char *name,const void *data,unsigned int len,bool secure) { return (_dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,data,len,(int)secure) == 0); }
|
inline bool dataStorePut(const char *name,const void *data,unsigned int len,bool secure) { return (_cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,data,len,(int)secure) == 0); }
|
||||||
inline bool dataStorePut(const char *name,const std::string &data,bool secure) { return dataStorePut(name,(const void *)data.data(),(unsigned int)data.length(),secure); }
|
inline bool dataStorePut(const char *name,const std::string &data,bool secure) { return dataStorePut(name,(const void *)data.data(),(unsigned int)data.length(),secure); }
|
||||||
inline void dataStoreDelete(const char *name) { _dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,(const void *)0,0,0); }
|
inline void dataStoreDelete(const char *name) { _cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,(const void *)0,0,0); }
|
||||||
std::string dataStoreGet(const char *name);
|
std::string dataStoreGet(const char *name);
|
||||||
|
|
||||||
/**
|
inline void postEvent(ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,ev,md); }
|
||||||
* Post an event to the external user
|
|
||||||
*
|
|
||||||
* @param ev Event type
|
|
||||||
* @param md Meta-data (default: NULL/none)
|
|
||||||
*/
|
|
||||||
inline void postEvent(ZT_Event ev,const void *md = (const void *)0) { _eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,ev,md); }
|
|
||||||
|
|
||||||
/**
|
inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); }
|
||||||
* Update virtual network port configuration
|
|
||||||
*
|
|
||||||
* @param nwid Network ID
|
|
||||||
* @param nuptr Network user ptr
|
|
||||||
* @param op Configuration operation
|
|
||||||
* @param nc Network configuration
|
|
||||||
*/
|
|
||||||
inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); }
|
|
||||||
|
|
||||||
inline bool online() const throw() { return _online; }
|
inline bool online() const throw() { return _online; }
|
||||||
|
|
||||||
@@ -254,10 +206,74 @@ public:
|
|||||||
void postTrace(const char *module,unsigned int line,const char *fmt,...);
|
void postTrace(const char *module,unsigned int line,const char *fmt,...);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool shouldUsePathForZeroTierTraffic(const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress);
|
||||||
|
inline bool externalPathLookup(const Address &ztaddr,int family,InetAddress &addr) { return ( (_cb.pathLookupFunction) ? (_cb.pathLookupFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,ztaddr.toInt(),family,reinterpret_cast<struct sockaddr_storage *>(&addr)) != 0) : false ); }
|
||||||
|
|
||||||
uint64_t prng();
|
uint64_t prng();
|
||||||
void postCircuitTestReport(const ZT_CircuitTestReport *report);
|
void postCircuitTestReport(const ZT_CircuitTestReport *report);
|
||||||
void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
|
void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
|
||||||
|
|
||||||
|
World planet() const;
|
||||||
|
std::vector<World> moons() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register that we are expecting a reply to a packet ID
|
||||||
|
*
|
||||||
|
* This only uses the most significant bits of the packet ID, both to save space
|
||||||
|
* and to avoid using the higher bits that can be modified during armor() to
|
||||||
|
* mask against the packet send counter used for QoS detection.
|
||||||
|
*
|
||||||
|
* @param packetId Packet ID to expect reply to
|
||||||
|
*/
|
||||||
|
inline void expectReplyTo(const uint64_t packetId)
|
||||||
|
{
|
||||||
|
const unsigned long pid2 = (unsigned long)(packetId >> 32);
|
||||||
|
const unsigned long bucket = (unsigned long)(pid2 & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
|
||||||
|
_expectingRepliesTo[bucket][_expectingRepliesToBucketPtr[bucket]++ & ZT_EXPECTING_REPLIES_BUCKET_MASK2] = (uint32_t)pid2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a given packet ID is something we are expecting a reply to
|
||||||
|
*
|
||||||
|
* This only uses the most significant bits of the packet ID, both to save space
|
||||||
|
* and to avoid using the higher bits that can be modified during armor() to
|
||||||
|
* mask against the packet send counter used for QoS detection.
|
||||||
|
*
|
||||||
|
* @param packetId Packet ID to check
|
||||||
|
* @return True if we're expecting a reply
|
||||||
|
*/
|
||||||
|
inline bool expectingReplyTo(const uint64_t packetId) const
|
||||||
|
{
|
||||||
|
const uint32_t pid2 = (uint32_t)(packetId >> 32);
|
||||||
|
const unsigned long bucket = (unsigned long)(pid2 & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
|
||||||
|
for(unsigned long i=0;i<=ZT_EXPECTING_REPLIES_BUCKET_MASK2;++i) {
|
||||||
|
if (_expectingRepliesTo[bucket][i] == pid2)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether we should do potentially expensive identity verification (rate limit)
|
||||||
|
*
|
||||||
|
* @param now Current time
|
||||||
|
* @param from Source address of packet
|
||||||
|
* @return True if within rate limits
|
||||||
|
*/
|
||||||
|
inline bool rateGateIdentityVerification(const uint64_t now,const InetAddress &from)
|
||||||
|
{
|
||||||
|
unsigned long iph = from.rateGateHash();
|
||||||
|
if ((now - _lastIdentityVerification[iph]) >= ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT) {
|
||||||
|
_lastIdentityVerification[iph] = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig);
|
||||||
|
virtual void ncSendRevocation(const Address &destination,const Revocation &rev);
|
||||||
|
virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline SharedPtr<Network> _network(uint64_t nwid) const
|
inline SharedPtr<Network> _network(uint64_t nwid) const
|
||||||
{
|
{
|
||||||
@@ -271,16 +287,15 @@ private:
|
|||||||
|
|
||||||
RuntimeEnvironment _RR;
|
RuntimeEnvironment _RR;
|
||||||
RuntimeEnvironment *RR;
|
RuntimeEnvironment *RR;
|
||||||
|
|
||||||
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
|
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
|
||||||
|
ZT_Node_Callbacks _cb;
|
||||||
|
|
||||||
ZT_DataStoreGetFunction _dataStoreGetFunction;
|
// For tracking packet IDs to filter out OK/ERROR replies to packets we did not send
|
||||||
ZT_DataStorePutFunction _dataStorePutFunction;
|
uint8_t _expectingRepliesToBucketPtr[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1];
|
||||||
ZT_WirePacketSendFunction _wirePacketSendFunction;
|
uint32_t _expectingRepliesTo[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1][ZT_EXPECTING_REPLIES_BUCKET_MASK2 + 1];
|
||||||
ZT_VirtualNetworkFrameFunction _virtualNetworkFrameFunction;
|
|
||||||
ZT_VirtualNetworkConfigFunction _virtualNetworkConfigFunction;
|
// Time of last identity verification indexed by InetAddress.rateGateHash() -- used in IncomingPacket::_doHELLO() via rateGateIdentityVerification()
|
||||||
ZT_PathCheckFunction _pathCheckFunction;
|
uint64_t _lastIdentityVerification[16384];
|
||||||
ZT_EventCallback _eventCallback;
|
|
||||||
|
|
||||||
std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
|
std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
|
||||||
Mutex _networks_m;
|
Mutex _networks_m;
|
||||||
@@ -295,7 +310,7 @@ private:
|
|||||||
|
|
||||||
unsigned int _prngStreamPtr;
|
unsigned int _prngStreamPtr;
|
||||||
Salsa20 _prng;
|
Salsa20 _prng;
|
||||||
uint64_t _prngStream[16]; // repeatedly encrypted with _prng to yield a high-quality non-crypto PRNG stream
|
uint64_t _prngStream[ZT_NODE_PRNG_BUF_SIZE]; // repeatedly encrypted with _prng to yield a high-quality non-crypto PRNG stream
|
||||||
|
|
||||||
uint64_t _now;
|
uint64_t _now;
|
||||||
uint64_t _lastPingCheck;
|
uint64_t _lastPingCheck;
|
||||||
|
|||||||
@@ -21,8 +21,9 @@
|
|||||||
#include "OutboundMulticast.hpp"
|
#include "OutboundMulticast.hpp"
|
||||||
#include "Switch.hpp"
|
#include "Switch.hpp"
|
||||||
#include "Network.hpp"
|
#include "Network.hpp"
|
||||||
#include "CertificateOfMembership.hpp"
|
|
||||||
#include "Node.hpp"
|
#include "Node.hpp"
|
||||||
|
#include "Peer.hpp"
|
||||||
|
#include "Topology.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ void OutboundMulticast::init(
|
|||||||
const RuntimeEnvironment *RR,
|
const RuntimeEnvironment *RR,
|
||||||
uint64_t timestamp,
|
uint64_t timestamp,
|
||||||
uint64_t nwid,
|
uint64_t nwid,
|
||||||
const CertificateOfMembership *com,
|
bool disableCompression,
|
||||||
unsigned int limit,
|
unsigned int limit,
|
||||||
unsigned int gatherLimit,
|
unsigned int gatherLimit,
|
||||||
const MAC &src,
|
const MAC &src,
|
||||||
@@ -39,16 +40,25 @@ void OutboundMulticast::init(
|
|||||||
const void *payload,
|
const void *payload,
|
||||||
unsigned int len)
|
unsigned int len)
|
||||||
{
|
{
|
||||||
|
uint8_t flags = 0;
|
||||||
|
|
||||||
_timestamp = timestamp;
|
_timestamp = timestamp;
|
||||||
_nwid = nwid;
|
_nwid = nwid;
|
||||||
|
if (src) {
|
||||||
|
_macSrc = src;
|
||||||
|
flags |= 0x04;
|
||||||
|
} else {
|
||||||
|
_macSrc.fromAddress(RR->identity.address(),nwid);
|
||||||
|
}
|
||||||
|
_macDest = dest.mac();
|
||||||
_limit = limit;
|
_limit = limit;
|
||||||
|
_frameLen = (len < ZT_MAX_MTU) ? len : ZT_MAX_MTU;
|
||||||
|
_etherType = etherType;
|
||||||
|
|
||||||
uint8_t flags = 0;
|
|
||||||
if (gatherLimit) flags |= 0x02;
|
if (gatherLimit) flags |= 0x02;
|
||||||
if (src) flags |= 0x04;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TRACE(">>MC %.16llx INIT %.16llx/%s limit %u gatherLimit %u from %s to %s length %u com==%d",
|
TRACE(">>MC %.16llx INIT %.16llx/%s limit %u gatherLimit %u from %s to %s length %u",
|
||||||
(unsigned long long)this,
|
(unsigned long long)this,
|
||||||
nwid,
|
nwid,
|
||||||
dest.toString().c_str(),
|
dest.toString().c_str(),
|
||||||
@@ -56,58 +66,36 @@ void OutboundMulticast::init(
|
|||||||
gatherLimit,
|
gatherLimit,
|
||||||
(src) ? src.toString().c_str() : MAC(RR->identity.address(),nwid).toString().c_str(),
|
(src) ? src.toString().c_str() : MAC(RR->identity.address(),nwid).toString().c_str(),
|
||||||
dest.toString().c_str(),
|
dest.toString().c_str(),
|
||||||
len,
|
len);
|
||||||
(com) ? 1 : 0);
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
_packetNoCom.setSource(RR->identity.address());
|
_packet.setSource(RR->identity.address());
|
||||||
_packetNoCom.setVerb(Packet::VERB_MULTICAST_FRAME);
|
_packet.setVerb(Packet::VERB_MULTICAST_FRAME);
|
||||||
_packetNoCom.append((uint64_t)nwid);
|
_packet.append((uint64_t)nwid);
|
||||||
_packetNoCom.append(flags);
|
_packet.append(flags);
|
||||||
if (gatherLimit) _packetNoCom.append((uint32_t)gatherLimit);
|
if (gatherLimit) _packet.append((uint32_t)gatherLimit);
|
||||||
if (src) src.appendTo(_packetNoCom);
|
if (src) src.appendTo(_packet);
|
||||||
dest.mac().appendTo(_packetNoCom);
|
dest.mac().appendTo(_packet);
|
||||||
_packetNoCom.append((uint32_t)dest.adi());
|
_packet.append((uint32_t)dest.adi());
|
||||||
_packetNoCom.append((uint16_t)etherType);
|
_packet.append((uint16_t)etherType);
|
||||||
_packetNoCom.append(payload,len);
|
_packet.append(payload,_frameLen);
|
||||||
_packetNoCom.compress();
|
if (!disableCompression)
|
||||||
|
_packet.compress();
|
||||||
|
|
||||||
if (com) {
|
memcpy(_frameData,payload,_frameLen);
|
||||||
_haveCom = true;
|
|
||||||
flags |= 0x01;
|
|
||||||
|
|
||||||
_packetWithCom.setSource(RR->identity.address());
|
|
||||||
_packetWithCom.setVerb(Packet::VERB_MULTICAST_FRAME);
|
|
||||||
_packetWithCom.append((uint64_t)nwid);
|
|
||||||
_packetWithCom.append(flags);
|
|
||||||
com->serialize(_packetWithCom);
|
|
||||||
if (gatherLimit) _packetWithCom.append((uint32_t)gatherLimit);
|
|
||||||
if (src) src.appendTo(_packetWithCom);
|
|
||||||
dest.mac().appendTo(_packetWithCom);
|
|
||||||
_packetWithCom.append((uint32_t)dest.adi());
|
|
||||||
_packetWithCom.append((uint16_t)etherType);
|
|
||||||
_packetWithCom.append(payload,len);
|
|
||||||
_packetWithCom.compress();
|
|
||||||
} else _haveCom = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toAddr)
|
void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toAddr)
|
||||||
{
|
{
|
||||||
if (_haveCom) {
|
const SharedPtr<Network> nw(RR->node->network(_nwid));
|
||||||
SharedPtr<Peer> peer(RR->topology->getPeer(toAddr));
|
const Address toAddr2(toAddr);
|
||||||
if ( (!peer) || (peer->needsOurNetworkMembershipCertificate(_nwid,RR->node->now(),true)) ) {
|
if ((nw)&&(nw->filterOutgoingPacket(true,RR->identity.address(),toAddr2,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) {
|
||||||
//TRACE(">>MC %.16llx -> %s (with COM)",(unsigned long long)this,toAddr.toString().c_str());
|
//TRACE(">>MC %.16llx -> %s",(unsigned long long)this,toAddr.toString().c_str());
|
||||||
_packetWithCom.newInitializationVector();
|
_packet.newInitializationVector();
|
||||||
_packetWithCom.setDestination(toAddr);
|
_packet.setDestination(toAddr2);
|
||||||
RR->sw->send(_packetWithCom,true,_nwid);
|
RR->node->expectReplyTo(_packet.packetId());
|
||||||
return;
|
RR->sw->send(_packet,true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TRACE(">>MC %.16llx -> %s (without COM)",(unsigned long long)this,toAddr.toString().c_str());
|
|
||||||
_packetNoCom.newInitializationVector();
|
|
||||||
_packetNoCom.setDestination(toAddr);
|
|
||||||
RR->sw->send(_packetNoCom,true,_nwid);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public:
|
|||||||
* @param RR Runtime environment
|
* @param RR Runtime environment
|
||||||
* @param timestamp Creation time
|
* @param timestamp Creation time
|
||||||
* @param nwid Network ID
|
* @param nwid Network ID
|
||||||
* @param com Certificate of membership or NULL if none available
|
* @param disableCompression Disable compression of frame payload
|
||||||
* @param limit Multicast limit for desired number of packets to send
|
* @param limit Multicast limit for desired number of packets to send
|
||||||
* @param gatherLimit Number to lazily/implicitly gather with this frame or 0 for none
|
* @param gatherLimit Number to lazily/implicitly gather with this frame or 0 for none
|
||||||
* @param src Source MAC address of frame or NULL to imply compute from sender ZT address
|
* @param src Source MAC address of frame or NULL to imply compute from sender ZT address
|
||||||
@@ -70,7 +70,7 @@ public:
|
|||||||
const RuntimeEnvironment *RR,
|
const RuntimeEnvironment *RR,
|
||||||
uint64_t timestamp,
|
uint64_t timestamp,
|
||||||
uint64_t nwid,
|
uint64_t nwid,
|
||||||
const CertificateOfMembership *com,
|
bool disableCompression,
|
||||||
unsigned int limit,
|
unsigned int limit,
|
||||||
unsigned int gatherLimit,
|
unsigned int gatherLimit,
|
||||||
const MAC &src,
|
const MAC &src,
|
||||||
@@ -127,17 +127,22 @@ public:
|
|||||||
if (std::find(_alreadySentTo.begin(),_alreadySentTo.end(),toAddr) == _alreadySentTo.end()) {
|
if (std::find(_alreadySentTo.begin(),_alreadySentTo.end(),toAddr) == _alreadySentTo.end()) {
|
||||||
sendAndLog(RR,toAddr);
|
sendAndLog(RR,toAddr);
|
||||||
return true;
|
return true;
|
||||||
} else return false;
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t _timestamp;
|
uint64_t _timestamp;
|
||||||
uint64_t _nwid;
|
uint64_t _nwid;
|
||||||
|
MAC _macSrc;
|
||||||
|
MAC _macDest;
|
||||||
unsigned int _limit;
|
unsigned int _limit;
|
||||||
Packet _packetNoCom;
|
unsigned int _frameLen;
|
||||||
Packet _packetWithCom;
|
unsigned int _etherType;
|
||||||
|
Packet _packet;
|
||||||
std::vector<Address> _alreadySentTo;
|
std::vector<Address> _alreadySentTo;
|
||||||
bool _haveCom;
|
uint8_t _frameData[ZT_MAX_MTU];
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -34,11 +34,11 @@
|
|||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "Buffer.hpp"
|
#include "Buffer.hpp"
|
||||||
|
|
||||||
#ifdef ZT_USE_SYSTEM_LZ4
|
//#ifdef ZT_USE_SYSTEM_LZ4
|
||||||
#include <lz4.h>
|
//#include <lz4.h>
|
||||||
#else
|
//#else
|
||||||
#include "../ext/lz4/lz4.h"
|
//#include "../ext/lz4/lz4.h"
|
||||||
#endif
|
//#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Protocol version -- incremented only for major changes
|
* Protocol version -- incremented only for major changes
|
||||||
@@ -51,19 +51,25 @@
|
|||||||
* + Yet another multicast redesign
|
* + Yet another multicast redesign
|
||||||
* + New crypto completely changes key agreement cipher
|
* + New crypto completely changes key agreement cipher
|
||||||
* 4 - 0.6.0 ... 1.0.6
|
* 4 - 0.6.0 ... 1.0.6
|
||||||
* + New identity format based on hashcash design
|
* + BREAKING CHANGE: New identity format based on hashcash design
|
||||||
* 5 - 1.1.0 ... 1.1.5
|
* 5 - 1.1.0 ... 1.1.5
|
||||||
* + Supports circuit test, proof of work, and echo
|
* + Supports circuit test, proof of work, and echo
|
||||||
* + Supports in-band world (root server definition) updates
|
* + Supports in-band world (root server definition) updates
|
||||||
* + Clustering! (Though this will work with protocol v4 clients.)
|
* + Clustering! (Though this will work with protocol v4 clients.)
|
||||||
* + Otherwise backward compatible with protocol v4
|
* + Otherwise backward compatible with protocol v4
|
||||||
* 6 - 1.1.5 ... 1.1.10
|
* 6 - 1.1.5 ... 1.1.10
|
||||||
* + Deprecate old dictionary-based network config format
|
* + Network configuration format revisions including binary values
|
||||||
* + Introduce new binary serialized network config and meta-data
|
* 7 - 1.1.10 ... 1.1.17
|
||||||
* 7 - 1.1.10 -- CURRENT
|
|
||||||
* + Introduce trusted paths for local SDN use
|
* + Introduce trusted paths for local SDN use
|
||||||
|
* 8 - 1.1.17 ... 1.2.0
|
||||||
|
* + Multipart network configurations for large network configs
|
||||||
|
* + Tags and Capabilities
|
||||||
|
* + Inline push of CertificateOfMembership deprecated
|
||||||
|
* + Certificates of representation for federation and mesh
|
||||||
|
* 9 - 1.2.0 ... CURRENT
|
||||||
|
* + In-band encoding of packet counter for link quality measurement
|
||||||
*/
|
*/
|
||||||
#define ZT_PROTO_VERSION 7
|
#define ZT_PROTO_VERSION 9
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimum supported protocol version
|
* Minimum supported protocol version
|
||||||
@@ -303,6 +309,7 @@
|
|||||||
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS + 1)
|
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS + 1)
|
||||||
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC + 6)
|
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC + 6)
|
||||||
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI + 4)
|
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI + 4)
|
||||||
|
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT + 4)
|
||||||
|
|
||||||
// Note: COM, GATHER_LIMIT, and SOURCE_MAC are optional, and so are specified without size
|
// Note: COM, GATHER_LIMIT, and SOURCE_MAC are optional, and so are specified without size
|
||||||
#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD)
|
#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD)
|
||||||
@@ -346,7 +353,7 @@ namespace ZeroTier {
|
|||||||
* ZeroTier packet
|
* ZeroTier packet
|
||||||
*
|
*
|
||||||
* Packet format:
|
* Packet format:
|
||||||
* <[8] 64-bit random packet ID and crypto initialization vector>
|
* <[8] 64-bit packet ID / crypto IV / packet counter>
|
||||||
* <[5] destination ZT address>
|
* <[5] destination ZT address>
|
||||||
* <[5] source ZT address>
|
* <[5] source ZT address>
|
||||||
* <[1] flags/cipher/hops>
|
* <[1] flags/cipher/hops>
|
||||||
@@ -357,6 +364,14 @@ namespace ZeroTier {
|
|||||||
*
|
*
|
||||||
* Packets smaller than 28 bytes are invalid and silently discarded.
|
* Packets smaller than 28 bytes are invalid and silently discarded.
|
||||||
*
|
*
|
||||||
|
* The 64-bit packet ID is a strongly random value used as a crypto IV.
|
||||||
|
* Its least significant 3 bits are also used as a monotonically increasing
|
||||||
|
* (and looping) counter for sending packets to a particular recipient. This
|
||||||
|
* can be used for link quality monitoring and reporting and has no crypto
|
||||||
|
* impact as it does not increase the likelihood of an IV collision. (The
|
||||||
|
* crypto we use is not sensitive to the nature of the IV, only that it does
|
||||||
|
* not repeat.)
|
||||||
|
*
|
||||||
* The flags/cipher/hops bit field is: FFCCCHHH where C is a 3-bit cipher
|
* The flags/cipher/hops bit field is: FFCCCHHH where C is a 3-bit cipher
|
||||||
* selection allowing up to 7 cipher suites, F is outside-envelope flags,
|
* selection allowing up to 7 cipher suites, F is outside-envelope flags,
|
||||||
* and H is hop count.
|
* and H is hop count.
|
||||||
@@ -523,50 +538,60 @@ public:
|
|||||||
/**
|
/**
|
||||||
* No operation (ignored, no reply)
|
* No operation (ignored, no reply)
|
||||||
*/
|
*/
|
||||||
VERB_NOP = 0,
|
VERB_NOP = 0x00,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Announcement of a node's existence:
|
* Announcement of a node's existence and vitals:
|
||||||
* <[1] protocol version>
|
* <[1] protocol version>
|
||||||
* <[1] software major version>
|
* <[1] software major version>
|
||||||
* <[1] software minor version>
|
* <[1] software minor version>
|
||||||
* <[2] software revision>
|
* <[2] software revision>
|
||||||
* <[8] timestamp (ms since epoch)>
|
* <[8] timestamp for determining latency>
|
||||||
* <[...] binary serialized identity (see Identity)>
|
* <[...] binary serialized identity (see Identity)>
|
||||||
* <[1] destination address type>
|
* <[...] physical destination address of packet>
|
||||||
* [<[...] destination address>]
|
* <[8] 64-bit world ID of current planet>
|
||||||
* <[8] 64-bit world ID of current world>
|
* <[8] 64-bit timestamp of current planet>
|
||||||
* <[8] 64-bit timestamp of current world>
|
* [... remainder if packet is encrypted using cryptField() ...]
|
||||||
|
* <[2] 16-bit number of moons>
|
||||||
|
* [<[1] 8-bit type ID of moon>]
|
||||||
|
* [<[8] 64-bit world ID of moon>]
|
||||||
|
* [<[8] 64-bit timestamp of moon>]
|
||||||
|
* [... additional moon type/ID/timestamp tuples ...]
|
||||||
|
* <[2] 16-bit length of certificate of representation>
|
||||||
|
* [... certificate of representation ...]
|
||||||
*
|
*
|
||||||
* This is the only message that ever must be sent in the clear, since it
|
* HELLO is sent in the clear as it is how peers share their identity
|
||||||
* is used to push an identity to a new peer.
|
* public keys. A few additional fields are sent in the clear too, but
|
||||||
|
* these are things that are public info or are easy to determine. As
|
||||||
|
* of 1.2.0 we have added a few more fields, but since these could have
|
||||||
|
* the potential to be sensitive we introduced the encryption of the
|
||||||
|
* remainder of the packet. See cryptField(). Packet MAC is still
|
||||||
|
* performed of course, so authentication occurs as normal.
|
||||||
*
|
*
|
||||||
* The destination address is the wire address to which this packet is
|
* Destination address is the actual wire address to which the packet
|
||||||
* being sent, and in OK is *also* the destination address of the OK
|
* was sent. See InetAddress::serialize() for format.
|
||||||
* packet. This can be used by the receiver to detect NAT, learn its real
|
|
||||||
* external address if behind NAT, and detect changes to its external
|
|
||||||
* address that require re-establishing connectivity.
|
|
||||||
*
|
|
||||||
* Destination address types and formats (not all of these are used now):
|
|
||||||
* 0x00 - None -- no destination address data present
|
|
||||||
* 0x01 - Ethernet address -- format: <[6] Ethernet MAC>
|
|
||||||
* 0x04 - 6-byte IPv4 UDP address/port -- format: <[4] IP>, <[2] port>
|
|
||||||
* 0x06 - 18-byte IPv6 UDP address/port -- format: <[16] IP>, <[2] port>
|
|
||||||
*
|
*
|
||||||
* OK payload:
|
* OK payload:
|
||||||
* <[8] timestamp (echoed from original HELLO)>
|
* <[8] HELLO timestamp field echo>
|
||||||
* <[1] protocol version (of responder)>
|
* <[1] protocol version>
|
||||||
* <[1] software major version (of responder)>
|
* <[1] software major version>
|
||||||
* <[1] software minor version (of responder)>
|
* <[1] software minor version>
|
||||||
* <[2] software revision (of responder)>
|
* <[2] software revision>
|
||||||
* <[1] destination address type (for this OK, not copied from HELLO)>
|
* <[...] physical destination address of packet>
|
||||||
* [<[...] destination address>]
|
* <[2] 16-bit length of world update(s) or 0 if none>
|
||||||
* <[2] 16-bit length of world update or 0 if none>
|
* [[...] updates to planets and/or moons]
|
||||||
* [[...] world update]
|
* <[2] 16-bit length of certificate of representation>
|
||||||
|
* [... certificate of representation ...]
|
||||||
|
*
|
||||||
|
* With the exception of the timestamp, the other fields pertain to the
|
||||||
|
* respondent who is sending OK and are not echoes.
|
||||||
|
*
|
||||||
|
* Note that OK is fully encrypted so no selective cryptField() of
|
||||||
|
* potentially sensitive fields is needed.
|
||||||
*
|
*
|
||||||
* ERROR has no payload.
|
* ERROR has no payload.
|
||||||
*/
|
*/
|
||||||
VERB_HELLO = 1,
|
VERB_HELLO = 0x01,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error response:
|
* Error response:
|
||||||
@@ -575,7 +600,7 @@ public:
|
|||||||
* <[1] error code>
|
* <[1] error code>
|
||||||
* <[...] error-dependent payload>
|
* <[...] error-dependent payload>
|
||||||
*/
|
*/
|
||||||
VERB_ERROR = 2,
|
VERB_ERROR = 0x02,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Success response:
|
* Success response:
|
||||||
@@ -583,50 +608,43 @@ public:
|
|||||||
* <[8] in-re packet ID>
|
* <[8] in-re packet ID>
|
||||||
* <[...] request-specific payload>
|
* <[...] request-specific payload>
|
||||||
*/
|
*/
|
||||||
VERB_OK = 3,
|
VERB_OK = 0x03,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query an identity by address:
|
* Query an identity by address:
|
||||||
* <[5] address to look up>
|
* <[5] address to look up>
|
||||||
|
* [<[...] additional addresses to look up>
|
||||||
*
|
*
|
||||||
* OK response payload:
|
* OK response payload:
|
||||||
* <[...] binary serialized identity>
|
* <[...] binary serialized identity>
|
||||||
|
* [<[...] additional binary serialized identities>]
|
||||||
*
|
*
|
||||||
* If querying a cluster, duplicate OK responses may occasionally occur.
|
* If querying a cluster, duplicate OK responses may occasionally occur.
|
||||||
* These should be discarded.
|
* These must be tolerated, which is easy since they'll have info you
|
||||||
|
* already have.
|
||||||
*
|
*
|
||||||
* If the address is not found, no response is generated. WHOIS requests
|
* If the address is not found, no response is generated. The semantics
|
||||||
* will time out much like ARP requests and similar do in L2.
|
* of WHOIS is similar to ARP and NDP in that persistent retrying can
|
||||||
|
* be performed.
|
||||||
*/
|
*/
|
||||||
VERB_WHOIS = 4,
|
VERB_WHOIS = 0x04,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Meet another node at a given protocol address:
|
* Relay-mediated NAT traversal or firewall punching initiation:
|
||||||
* <[1] flags (unused, currently 0)>
|
* <[1] flags (unused, currently 0)>
|
||||||
* <[5] ZeroTier address of peer that might be found at this address>
|
* <[5] ZeroTier address of peer that might be found at this address>
|
||||||
* <[2] 16-bit protocol address port>
|
* <[2] 16-bit protocol address port>
|
||||||
* <[1] protocol address length (4 for IPv4, 16 for IPv6)>
|
* <[1] protocol address length (4 for IPv4, 16 for IPv6)>
|
||||||
* <[...] protocol address (network byte order)>
|
* <[...] protocol address (network byte order)>
|
||||||
*
|
*
|
||||||
* This is sent by a relaying node to initiate NAT traversal between two
|
* An upstream node can send this to inform both sides of a relay of
|
||||||
* peers that are communicating by way of indirect relay. The relay will
|
* information they might use to establish a direct connection.
|
||||||
* send this to both peers at the same time on a periodic basis, telling
|
|
||||||
* each where it might find the other on the network.
|
|
||||||
*
|
*
|
||||||
* Upon receipt a peer sends HELLO to establish a direct link.
|
* Upon receipt a peer sends HELLO to establish a direct link.
|
||||||
*
|
*
|
||||||
* Nodes should implement rate control, limiting the rate at which they
|
|
||||||
* respond to these packets to prevent their use in DDOS attacks. Nodes
|
|
||||||
* may also ignore these messages if a peer is not known or is not being
|
|
||||||
* actively communicated with.
|
|
||||||
*
|
|
||||||
* Unfortunately the physical address format in this message pre-dates
|
|
||||||
* InetAddress's serialization format. :( ZeroTier is four years old and
|
|
||||||
* yes we've accumulated a tiny bit of cruft here and there.
|
|
||||||
*
|
|
||||||
* No OK or ERROR is generated.
|
* No OK or ERROR is generated.
|
||||||
*/
|
*/
|
||||||
VERB_RENDEZVOUS = 5,
|
VERB_RENDEZVOUS = 0x05,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME):
|
* ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME):
|
||||||
@@ -642,31 +660,44 @@ public:
|
|||||||
* ERROR may be generated if a membership certificate is needed for a
|
* ERROR may be generated if a membership certificate is needed for a
|
||||||
* closed network. Payload will be network ID.
|
* closed network. Payload will be network ID.
|
||||||
*/
|
*/
|
||||||
VERB_FRAME = 6,
|
VERB_FRAME = 0x06,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Full Ethernet frame with MAC addressing and optional fields:
|
* Full Ethernet frame with MAC addressing and optional fields:
|
||||||
* <[8] 64-bit network ID>
|
* <[8] 64-bit network ID>
|
||||||
* <[1] flags>
|
* <[1] flags>
|
||||||
* [<[...] certificate of network membership>]
|
|
||||||
* <[6] destination MAC or all zero for destination node>
|
* <[6] destination MAC or all zero for destination node>
|
||||||
* <[6] source MAC or all zero for node of origin>
|
* <[6] source MAC or all zero for node of origin>
|
||||||
* <[2] 16-bit ethertype>
|
* <[2] 16-bit ethertype>
|
||||||
* <[...] ethernet payload>
|
* <[...] ethernet payload>
|
||||||
*
|
*
|
||||||
* Flags:
|
* Flags:
|
||||||
* 0x01 - Certificate of network membership is attached
|
* 0x01 - Certificate of network membership attached (DEPRECATED)
|
||||||
|
* 0x02 - Most significant bit of subtype (see below)
|
||||||
|
* 0x04 - Middle bit of subtype (see below)
|
||||||
|
* 0x08 - Least significant bit of subtype (see below)
|
||||||
|
* 0x10 - ACK requested in the form of OK(EXT_FRAME)
|
||||||
*
|
*
|
||||||
* An extended frame carries full MAC addressing, making them a
|
* Subtypes (0..7):
|
||||||
* superset of VERB_FRAME. They're used for bridging or when we
|
* 0x0 - Normal frame (bridging can be determined by checking MAC)
|
||||||
* want to attach a certificate since FRAME does not support that.
|
* 0x1 - TEEd outbound frame
|
||||||
|
* 0x2 - REDIRECTed outbound frame
|
||||||
|
* 0x3 - WATCHed outbound frame (TEE with ACK, ACK bit also set)
|
||||||
|
* 0x4 - TEEd inbound frame
|
||||||
|
* 0x5 - REDIRECTed inbound frame
|
||||||
|
* 0x6 - WATCHed inbound frame
|
||||||
|
* 0x7 - (reserved for future use)
|
||||||
*
|
*
|
||||||
* Multicast frames may not be sent as EXT_FRAME.
|
* An extended frame carries full MAC addressing, making it a
|
||||||
|
* superset of VERB_FRAME. It is used for bridged traffic,
|
||||||
|
* redirected or observed traffic via rules, and can in theory
|
||||||
|
* be used for multicast though MULTICAST_FRAME exists for that
|
||||||
|
* purpose and has additional options and capabilities.
|
||||||
*
|
*
|
||||||
* ERROR may be generated if a membership certificate is needed for a
|
* OK payload (if ACK flag is set):
|
||||||
* closed network. Payload will be network ID.
|
* <[8] 64-bit network ID>
|
||||||
*/
|
*/
|
||||||
VERB_EXT_FRAME = 7,
|
VERB_EXT_FRAME = 0x07,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ECHO request (a.k.a. ping):
|
* ECHO request (a.k.a. ping):
|
||||||
@@ -676,7 +707,7 @@ public:
|
|||||||
* is generated. Response to ECHO requests is optional and ECHO may be
|
* is generated. Response to ECHO requests is optional and ECHO may be
|
||||||
* ignored if a node detects a possible flood.
|
* ignored if a node detects a possible flood.
|
||||||
*/
|
*/
|
||||||
VERB_ECHO = 8,
|
VERB_ECHO = 0x08,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Announce interest in multicast group(s):
|
* Announce interest in multicast group(s):
|
||||||
@@ -690,77 +721,117 @@ public:
|
|||||||
* controllers and root servers. In the current network, root servers
|
* controllers and root servers. In the current network, root servers
|
||||||
* will provide the service of final multicast cache.
|
* will provide the service of final multicast cache.
|
||||||
*
|
*
|
||||||
* It is recommended that NETWORK_MEMBERSHIP_CERTIFICATE pushes be sent
|
* VERB_NETWORK_CREDENTIALS should be pushed along with this, especially
|
||||||
* along with MULTICAST_LIKE when pushing LIKEs to peers that do not
|
* if using upstream (e.g. root) nodes as multicast databases. This allows
|
||||||
* share a network membership (such as root servers), since this can be
|
* GATHERs to be authenticated.
|
||||||
* used to authenticate GATHER requests and limit responses to peers
|
|
||||||
* authorized to talk on a network. (Should be an optional field here,
|
|
||||||
* but saving one or two packets every five minutes is not worth an
|
|
||||||
* ugly hack or protocol rev.)
|
|
||||||
*
|
*
|
||||||
* OK/ERROR are not generated.
|
* OK/ERROR are not generated.
|
||||||
*/
|
*/
|
||||||
VERB_MULTICAST_LIKE = 9,
|
VERB_MULTICAST_LIKE = 0x09,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Network member certificate replication/push:
|
* Network credentials push:
|
||||||
* <[...] serialized certificate of membership>
|
* [<[...] one or more certificates of membership>]
|
||||||
* [ ... additional certificates may follow ...]
|
* <[1] 0x00, null byte marking end of COM array>
|
||||||
|
* <[2] 16-bit number of capabilities>
|
||||||
|
* <[...] one or more serialized Capability>
|
||||||
|
* <[2] 16-bit number of tags>
|
||||||
|
* <[...] one or more serialized Tags>
|
||||||
|
* <[2] 16-bit number of revocations>
|
||||||
|
* <[...] one or more serialized Revocations>
|
||||||
|
* <[2] 16-bit number of certificates of ownership>
|
||||||
|
* <[...] one or more serialized CertificateOfOwnership>
|
||||||
*
|
*
|
||||||
* This is sent in response to ERROR_NEED_MEMBERSHIP_CERTIFICATE and may
|
* This can be sent by anyone at any time to push network credentials.
|
||||||
* be pushed at any other time to keep exchanged certificates up to date.
|
* These will of course only be accepted if they are properly signed.
|
||||||
|
* Credentials can be for any number of networks.
|
||||||
|
*
|
||||||
|
* The use of a zero byte to terminate the COM section is for legacy
|
||||||
|
* backward compatiblity. Newer fields are prefixed with a length.
|
||||||
*
|
*
|
||||||
* OK/ERROR are not generated.
|
* OK/ERROR are not generated.
|
||||||
*/
|
*/
|
||||||
VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10,
|
VERB_NETWORK_CREDENTIALS = 0x0a,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Network configuration request:
|
* Network configuration request:
|
||||||
* <[8] 64-bit network ID>
|
* <[8] 64-bit network ID>
|
||||||
* <[2] 16-bit length of request meta-data dictionary>
|
* <[2] 16-bit length of request meta-data dictionary>
|
||||||
* <[...] string-serialized request meta-data>
|
* <[...] string-serialized request meta-data>
|
||||||
* [<[8] 64-bit revision of netconf we currently have>]
|
* <[8] 64-bit revision of netconf we currently have>
|
||||||
|
* <[8] 64-bit timestamp of netconf we currently have>
|
||||||
*
|
*
|
||||||
* This message requests network configuration from a node capable of
|
* This message requests network configuration from a node capable of
|
||||||
* providing it. If the optional revision is included, a response is
|
* providing it.
|
||||||
* only generated if there is a newer network configuration available.
|
*
|
||||||
|
* Respones to this are always whole configs intended for the recipient.
|
||||||
|
* For patches and other updates a NETWORK_CONFIG is sent instead.
|
||||||
|
*
|
||||||
|
* It would be valid and correct as of 1.2.0 to use NETWORK_CONFIG always,
|
||||||
|
* but OK(NTEWORK_CONFIG_REQUEST) should be sent for compatibility.
|
||||||
*
|
*
|
||||||
* OK response payload:
|
* OK response payload:
|
||||||
* <[8] 64-bit network ID>
|
* <[8] 64-bit network ID>
|
||||||
* <[2] 16-bit length of network configuration dictionary>
|
* <[2] 16-bit length of network configuration dictionary chunk>
|
||||||
* <[...] network configuration dictionary>
|
* <[...] network configuration dictionary (may be incomplete)>
|
||||||
|
* [ ... end of legacy single chunk response ... ]
|
||||||
|
* <[1] 8-bit flags>
|
||||||
|
* <[8] 64-bit config update ID (should never be 0)>
|
||||||
|
* <[4] 32-bit total length of assembled dictionary>
|
||||||
|
* <[4] 32-bit index of chunk>
|
||||||
|
* [ ... end signed portion ... ]
|
||||||
|
* <[1] 8-bit chunk signature type>
|
||||||
|
* <[2] 16-bit length of chunk signature>
|
||||||
|
* <[...] chunk signature>
|
||||||
*
|
*
|
||||||
* OK returns a Dictionary (string serialized) containing the network's
|
* The chunk signature signs the entire payload of the OK response.
|
||||||
* configuration and IP address assignment information for the querying
|
* Currently only one signature type is supported: ed25519 (1).
|
||||||
* node. It also contains a membership certificate that the querying
|
|
||||||
* node can push to other peers to demonstrate its right to speak on
|
|
||||||
* a given network.
|
|
||||||
*
|
*
|
||||||
* When a new network configuration is received, another config request
|
* Each config chunk is signed to prevent memory exhaustion or
|
||||||
* should be sent with the new netconf's revision. This confirms receipt
|
* traffic crowding DOS attacks against config fragment assembly.
|
||||||
* and also causes any subsequent changes to rapidly propagate as this
|
*
|
||||||
* cycle will repeat until there are no changes. This is optional but
|
* If the packet is from the network controller it is permitted to end
|
||||||
* recommended behavior.
|
* before the config update ID or other chunking related or signature
|
||||||
|
* fields. This is to support older controllers that don't include
|
||||||
|
* these fields and may be removed in the future.
|
||||||
*
|
*
|
||||||
* ERROR response payload:
|
* ERROR response payload:
|
||||||
* <[8] 64-bit network ID>
|
* <[8] 64-bit network ID>
|
||||||
*
|
|
||||||
* UNSUPPORTED_OPERATION is returned if this service is not supported,
|
|
||||||
* and OBJ_NOT_FOUND if the queried network ID was not found.
|
|
||||||
*/
|
*/
|
||||||
VERB_NETWORK_CONFIG_REQUEST = 11,
|
VERB_NETWORK_CONFIG_REQUEST = 0x0b,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Network configuration refresh request:
|
* Network configuration data push:
|
||||||
* <[...] array of 64-bit network IDs>
|
* <[8] 64-bit network ID>
|
||||||
|
* <[2] 16-bit length of network configuration dictionary chunk>
|
||||||
|
* <[...] network configuration dictionary (may be incomplete)>
|
||||||
|
* <[1] 8-bit flags>
|
||||||
|
* <[8] 64-bit config update ID (should never be 0)>
|
||||||
|
* <[4] 32-bit total length of assembled dictionary>
|
||||||
|
* <[4] 32-bit index of chunk>
|
||||||
|
* [ ... end signed portion ... ]
|
||||||
|
* <[1] 8-bit chunk signature type>
|
||||||
|
* <[2] 16-bit length of chunk signature>
|
||||||
|
* <[...] chunk signature>
|
||||||
*
|
*
|
||||||
* This can be sent by the network controller to inform a node that it
|
* This is a direct push variant for network config updates. It otherwise
|
||||||
* should now make a NETWORK_CONFIG_REQUEST.
|
* carries the same payload as OK(NETWORK_CONFIG_REQUEST) and has the same
|
||||||
|
* semantics.
|
||||||
*
|
*
|
||||||
* It does not generate an OK or ERROR message, and is treated only as
|
* The legacy mode missing the additional chunking fields is not supported
|
||||||
* a hint to refresh now.
|
* here.
|
||||||
|
*
|
||||||
|
* Flags:
|
||||||
|
* 0x01 - Use fast propagation
|
||||||
|
*
|
||||||
|
* An OK should be sent if the config is successfully received and
|
||||||
|
* accepted.
|
||||||
|
*
|
||||||
|
* OK payload:
|
||||||
|
* <[8] 64-bit network ID>
|
||||||
|
* <[8] 64-bit config update ID>
|
||||||
*/
|
*/
|
||||||
VERB_NETWORK_CONFIG_REFRESH = 12,
|
VERB_NETWORK_CONFIG = 0x0c,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request endpoints for multicast distribution:
|
* Request endpoints for multicast distribution:
|
||||||
@@ -772,7 +843,7 @@ public:
|
|||||||
* [<[...] network certificate of membership>]
|
* [<[...] network certificate of membership>]
|
||||||
*
|
*
|
||||||
* Flags:
|
* Flags:
|
||||||
* 0x01 - Network certificate of membership is attached
|
* 0x01 - COM is attached
|
||||||
*
|
*
|
||||||
* This message asks a peer for additional known endpoints that have
|
* This message asks a peer for additional known endpoints that have
|
||||||
* LIKEd a given multicast group. It's sent when the sender wishes
|
* LIKEd a given multicast group. It's sent when the sender wishes
|
||||||
@@ -782,6 +853,9 @@ public:
|
|||||||
* More than one OK response can occur if the response is broken up across
|
* More than one OK response can occur if the response is broken up across
|
||||||
* multiple packets or if querying a clustered node.
|
* multiple packets or if querying a clustered node.
|
||||||
*
|
*
|
||||||
|
* The COM should be included so that upstream nodes that are not
|
||||||
|
* members of our network can validate our request.
|
||||||
|
*
|
||||||
* OK response payload:
|
* OK response payload:
|
||||||
* <[8] 64-bit network ID>
|
* <[8] 64-bit network ID>
|
||||||
* <[6] MAC address of multicast group being queried>
|
* <[6] MAC address of multicast group being queried>
|
||||||
@@ -793,13 +867,12 @@ public:
|
|||||||
*
|
*
|
||||||
* ERROR is not generated; queries that return no response are dropped.
|
* ERROR is not generated; queries that return no response are dropped.
|
||||||
*/
|
*/
|
||||||
VERB_MULTICAST_GATHER = 13,
|
VERB_MULTICAST_GATHER = 0x0d,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multicast frame:
|
* Multicast frame:
|
||||||
* <[8] 64-bit network ID>
|
* <[8] 64-bit network ID>
|
||||||
* <[1] flags>
|
* <[1] flags>
|
||||||
* [<[...] network certificate of membership>]
|
|
||||||
* [<[4] 32-bit implicit gather limit>]
|
* [<[4] 32-bit implicit gather limit>]
|
||||||
* [<[6] source MAC>]
|
* [<[6] source MAC>]
|
||||||
* <[6] destination MAC (multicast address)>
|
* <[6] destination MAC (multicast address)>
|
||||||
@@ -808,7 +881,7 @@ public:
|
|||||||
* <[...] ethernet payload>
|
* <[...] ethernet payload>
|
||||||
*
|
*
|
||||||
* Flags:
|
* Flags:
|
||||||
* 0x01 - Network certificate of membership is attached
|
* 0x01 - Network certificate of membership attached (DEPRECATED)
|
||||||
* 0x02 - Implicit gather limit field is present
|
* 0x02 - Implicit gather limit field is present
|
||||||
* 0x04 - Source MAC is specified -- otherwise it's computed from sender
|
* 0x04 - Source MAC is specified -- otherwise it's computed from sender
|
||||||
*
|
*
|
||||||
@@ -823,11 +896,11 @@ public:
|
|||||||
* <[6] MAC address of multicast group>
|
* <[6] MAC address of multicast group>
|
||||||
* <[4] 32-bit ADI for multicast group>
|
* <[4] 32-bit ADI for multicast group>
|
||||||
* <[1] flags>
|
* <[1] flags>
|
||||||
* [<[...] network certficate of membership>]
|
* [<[...] network certficate of membership (DEPRECATED)>]
|
||||||
* [<[...] implicit gather results if flag 0x01 is set>]
|
* [<[...] implicit gather results if flag 0x01 is set>]
|
||||||
*
|
*
|
||||||
* OK flags (same bits as request flags):
|
* OK flags (same bits as request flags):
|
||||||
* 0x01 - OK includes certificate of network membership
|
* 0x01 - OK includes certificate of network membership (DEPRECATED)
|
||||||
* 0x02 - OK includes implicit gather results
|
* 0x02 - OK includes implicit gather results
|
||||||
*
|
*
|
||||||
* ERROR response payload:
|
* ERROR response payload:
|
||||||
@@ -835,7 +908,7 @@ public:
|
|||||||
* <[6] multicast group MAC>
|
* <[6] multicast group MAC>
|
||||||
* <[4] 32-bit multicast group ADI>
|
* <[4] 32-bit multicast group ADI>
|
||||||
*/
|
*/
|
||||||
VERB_MULTICAST_FRAME = 14,
|
VERB_MULTICAST_FRAME = 0x0e,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Push of potential endpoints for direct communication:
|
* Push of potential endpoints for direct communication:
|
||||||
@@ -871,7 +944,7 @@ public:
|
|||||||
*
|
*
|
||||||
* OK and ERROR are not generated.
|
* OK and ERROR are not generated.
|
||||||
*/
|
*/
|
||||||
VERB_PUSH_DIRECT_PATHS = 16,
|
VERB_PUSH_DIRECT_PATHS = 0x10,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Source-routed circuit test message:
|
* Source-routed circuit test message:
|
||||||
@@ -887,21 +960,17 @@ public:
|
|||||||
* [ ... end of signed portion of request ... ]
|
* [ ... end of signed portion of request ... ]
|
||||||
* <[2] 16-bit length of signature of request>
|
* <[2] 16-bit length of signature of request>
|
||||||
* <[...] signature of request by originator>
|
* <[...] signature of request by originator>
|
||||||
* <[2] 16-bit previous hop credential length (including type)>
|
* <[2] 16-bit length of additional fields>
|
||||||
* [[1] previous hop credential type]
|
* [[...] additional fields]
|
||||||
* [[...] previous hop credential]
|
|
||||||
* <[...] next hop(s) in path>
|
* <[...] next hop(s) in path>
|
||||||
*
|
*
|
||||||
* Flags:
|
* Flags:
|
||||||
* 0x01 - Report back to originator at middle hops
|
* 0x01 - Report back to originator at all hops
|
||||||
* 0x02 - Report back to originator at last hop
|
* 0x02 - Report back to originator at last hop
|
||||||
*
|
*
|
||||||
* Originator credential types:
|
* Originator credential types:
|
||||||
* 0x01 - 64-bit network ID for which originator is controller
|
* 0x01 - 64-bit network ID for which originator is controller
|
||||||
*
|
*
|
||||||
* Previous hop credential types:
|
|
||||||
* 0x01 - Certificate of network membership
|
|
||||||
*
|
|
||||||
* Path record format:
|
* Path record format:
|
||||||
* <[1] 8-bit flags (unused, must be zero)>
|
* <[1] 8-bit flags (unused, must be zero)>
|
||||||
* <[1] 8-bit breadth (number of next hops)>
|
* <[1] 8-bit breadth (number of next hops)>
|
||||||
@@ -950,29 +1019,28 @@ public:
|
|||||||
* <[8] 64-bit timestamp (echoed from original>
|
* <[8] 64-bit timestamp (echoed from original>
|
||||||
* <[8] 64-bit test ID (echoed from original)>
|
* <[8] 64-bit test ID (echoed from original)>
|
||||||
*/
|
*/
|
||||||
VERB_CIRCUIT_TEST = 17,
|
VERB_CIRCUIT_TEST = 0x11,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Circuit test hop report:
|
* Circuit test hop report:
|
||||||
* <[8] 64-bit timestamp (from original test)>
|
* <[8] 64-bit timestamp (echoed from original test)>
|
||||||
* <[8] 64-bit test ID (from original test)>
|
* <[8] 64-bit test ID (echoed from original test)>
|
||||||
* <[8] 64-bit reserved field (set to 0, currently unused)>
|
* <[8] 64-bit reserved field (set to 0, currently unused)>
|
||||||
* <[1] 8-bit vendor ID (set to 0, currently unused)>
|
* <[1] 8-bit vendor ID (set to 0, currently unused)>
|
||||||
* <[1] 8-bit reporter protocol version>
|
* <[1] 8-bit reporter protocol version>
|
||||||
* <[1] 8-bit reporter major version>
|
* <[1] 8-bit reporter software major version>
|
||||||
* <[1] 8-bit reporter minor version>
|
* <[1] 8-bit reporter software minor version>
|
||||||
* <[2] 16-bit reporter revision>
|
* <[2] 16-bit reporter software revision>
|
||||||
* <[2] 16-bit reporter OS/platform>
|
* <[2] 16-bit reporter OS/platform or 0 if not specified>
|
||||||
* <[2] 16-bit reporter architecture>
|
* <[2] 16-bit reporter architecture or 0 if not specified>
|
||||||
* <[2] 16-bit error code (set to 0, currently unused)>
|
* <[2] 16-bit error code (set to 0, currently unused)>
|
||||||
* <[8] 64-bit report flags (set to 0, currently unused)>
|
* <[8] 64-bit report flags>
|
||||||
* <[8] 64-bit source packet ID>
|
* <[8] 64-bit packet ID of received CIRCUIT_TEST packet>
|
||||||
* <[5] upstream ZeroTier address from which test was received>
|
* <[5] upstream ZeroTier address from which CIRCUIT_TEST was received>
|
||||||
* <[1] 8-bit source packet hop count (ZeroTier hop count)>
|
* <[1] 8-bit packet hop count of received CIRCUIT_TEST>
|
||||||
* <[...] local wire address on which packet was received>
|
* <[...] local wire address on which packet was received>
|
||||||
* <[...] remote wire address from which packet was received>
|
* <[...] remote wire address from which packet was received>
|
||||||
* <[2] 16-bit length of additional fields>
|
* <[2] 16-bit path link quality of path over which packet was received>
|
||||||
* <[...] additional fields>
|
|
||||||
* <[1] 8-bit number of next hops (breadth)>
|
* <[1] 8-bit number of next hops (breadth)>
|
||||||
* <[...] next hop information>
|
* <[...] next hop information>
|
||||||
*
|
*
|
||||||
@@ -980,6 +1048,9 @@ public:
|
|||||||
* <[5] ZeroTier address of next hop>
|
* <[5] ZeroTier address of next hop>
|
||||||
* <[...] current best direct path address, if any, 0 if none>
|
* <[...] current best direct path address, if any, 0 if none>
|
||||||
*
|
*
|
||||||
|
* Report flags:
|
||||||
|
* 0x1 - Upstream peer in circuit test path allowed in path (e.g. network COM valid)
|
||||||
|
*
|
||||||
* Circuit test reports can be sent by hops in a circuit test to report
|
* Circuit test reports can be sent by hops in a circuit test to report
|
||||||
* back results. They should include information about the sender as well
|
* back results. They should include information about the sender as well
|
||||||
* as about the paths to which next hops are being sent.
|
* as about the paths to which next hops are being sent.
|
||||||
@@ -987,50 +1058,22 @@ public:
|
|||||||
* If a test report is received and no circuit test was sent, it should be
|
* If a test report is received and no circuit test was sent, it should be
|
||||||
* ignored. This message generates no OK or ERROR response.
|
* ignored. This message generates no OK or ERROR response.
|
||||||
*/
|
*/
|
||||||
VERB_CIRCUIT_TEST_REPORT = 18,
|
VERB_CIRCUIT_TEST_REPORT = 0x12,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request proof of work:
|
* A message with arbitrary user-definable content:
|
||||||
* <[1] 8-bit proof of work type>
|
* <[8] 64-bit arbitrary message type ID>
|
||||||
* <[1] 8-bit proof of work difficulty>
|
* [<[...] message payload>]
|
||||||
* <[2] 16-bit length of proof of work challenge>
|
|
||||||
* <[...] proof of work challenge>
|
|
||||||
*
|
*
|
||||||
* This requests that a peer perform a proof of work calucation. It can be
|
* This can be used to send arbitrary messages over VL1. It generates no
|
||||||
* sent by highly trusted peers (e.g. root servers, network controllers)
|
* OK or ERROR and has no special semantics outside of whatever the user
|
||||||
* under suspected denial of service conditions in an attempt to filter
|
* (via the ZeroTier core API) chooses to give it.
|
||||||
* out "non-serious" peers and remain responsive to those proving their
|
|
||||||
* intent to actually communicate.
|
|
||||||
*
|
*
|
||||||
* If the peer obliges to perform the work, it does so and responds with
|
* Message type IDs less than or equal to 65535 are reserved for use by
|
||||||
* an OK containing the result. Otherwise it may ignore the message or
|
* ZeroTier, Inc. itself. We recommend making up random ones for your own
|
||||||
* response with an ERROR_INVALID_REQUEST or ERROR_UNSUPPORTED_OPERATION.
|
* implementations.
|
||||||
*
|
|
||||||
* Proof of work type IDs:
|
|
||||||
* 0x01 - Salsa20/12+SHA512 hashcash function
|
|
||||||
*
|
|
||||||
* Salsa20/12+SHA512 is based on the following composite hash function:
|
|
||||||
*
|
|
||||||
* (1) Compute SHA512(candidate)
|
|
||||||
* (2) Use the first 256 bits of the result of #1 as a key to encrypt
|
|
||||||
* 131072 zero bytes with Salsa20/12 (with a zero IV).
|
|
||||||
* (3) Compute SHA512(the result of step #2)
|
|
||||||
* (4) Accept this candiate if the first [difficulty] bits of the result
|
|
||||||
* from step #3 are zero. Otherwise generate a new candidate and try
|
|
||||||
* again.
|
|
||||||
*
|
|
||||||
* This is performed repeatedly on candidates generated by appending the
|
|
||||||
* supplied challenge to an arbitrary nonce until a valid candidate
|
|
||||||
* is found. This chosen prepended nonce is then returned as the result
|
|
||||||
* in OK.
|
|
||||||
*
|
|
||||||
* OK payload:
|
|
||||||
* <[2] 16-bit length of result>
|
|
||||||
* <[...] computed proof of work>
|
|
||||||
*
|
|
||||||
* ERROR has no payload.
|
|
||||||
*/
|
*/
|
||||||
VERB_REQUEST_PROOF_OF_WORK = 19
|
VERB_USER_MESSAGE = 0x14
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1039,39 +1082,37 @@ public:
|
|||||||
enum ErrorCode
|
enum ErrorCode
|
||||||
{
|
{
|
||||||
/* No error, not actually used in transit */
|
/* No error, not actually used in transit */
|
||||||
ERROR_NONE = 0,
|
ERROR_NONE = 0x00,
|
||||||
|
|
||||||
/* Invalid request */
|
/* Invalid request */
|
||||||
ERROR_INVALID_REQUEST = 1,
|
ERROR_INVALID_REQUEST = 0x01,
|
||||||
|
|
||||||
/* Bad/unsupported protocol version */
|
/* Bad/unsupported protocol version */
|
||||||
ERROR_BAD_PROTOCOL_VERSION = 2,
|
ERROR_BAD_PROTOCOL_VERSION = 0x02,
|
||||||
|
|
||||||
/* Unknown object queried */
|
/* Unknown object queried */
|
||||||
ERROR_OBJ_NOT_FOUND = 3,
|
ERROR_OBJ_NOT_FOUND = 0x03,
|
||||||
|
|
||||||
/* HELLO pushed an identity whose address is already claimed */
|
/* HELLO pushed an identity whose address is already claimed */
|
||||||
ERROR_IDENTITY_COLLISION = 4,
|
ERROR_IDENTITY_COLLISION = 0x04,
|
||||||
|
|
||||||
/* Verb or use case not supported/enabled by this node */
|
/* Verb or use case not supported/enabled by this node */
|
||||||
ERROR_UNSUPPORTED_OPERATION = 5,
|
ERROR_UNSUPPORTED_OPERATION = 0x05,
|
||||||
|
|
||||||
/* Message to private network rejected -- no unexpired certificate on file */
|
/* Network membership certificate update needed */
|
||||||
ERROR_NEED_MEMBERSHIP_CERTIFICATE = 6,
|
ERROR_NEED_MEMBERSHIP_CERTIFICATE = 0x06,
|
||||||
|
|
||||||
/* Tried to join network, but you're not a member */
|
/* Tried to join network, but you're not a member */
|
||||||
ERROR_NETWORK_ACCESS_DENIED_ = 7, /* extra _ to avoid Windows name conflict */
|
ERROR_NETWORK_ACCESS_DENIED_ = 0x07, /* extra _ at end to avoid Windows name conflict */
|
||||||
|
|
||||||
/* Multicasts to this group are not wanted */
|
/* Multicasts to this group are not wanted */
|
||||||
ERROR_UNWANTED_MULTICAST = 8
|
ERROR_UNWANTED_MULTICAST = 0x08
|
||||||
};
|
};
|
||||||
|
|
||||||
//#ifdef ZT_TRACE
|
#ifdef ZT_TRACE
|
||||||
static const char *verbString(Verb v)
|
static const char *verbString(Verb v);
|
||||||
throw();
|
static const char *errorString(ErrorCode e);
|
||||||
static const char *errorString(ErrorCode e)
|
#endif
|
||||||
throw();
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
template<unsigned int C2>
|
template<unsigned int C2>
|
||||||
Packet(const Buffer<C2> &b) :
|
Packet(const Buffer<C2> &b) :
|
||||||
@@ -1268,10 +1309,21 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Get this packet's unique ID (the IV field interpreted as uint64_t)
|
* Get this packet's unique ID (the IV field interpreted as uint64_t)
|
||||||
*
|
*
|
||||||
|
* Note that the least significant 3 bits of this ID will change when armor()
|
||||||
|
* is called to armor the packet for transport. This is because armor() will
|
||||||
|
* mask the last 3 bits against the send counter for QoS monitoring use prior
|
||||||
|
* to actually using the IV to encrypt and MAC the packet. Be aware of this
|
||||||
|
* when grabbing the packetId of a new packet prior to armor/send.
|
||||||
|
*
|
||||||
* @return Packet ID
|
* @return Packet ID
|
||||||
*/
|
*/
|
||||||
inline uint64_t packetId() const { return at<uint64_t>(ZT_PACKET_IDX_IV); }
|
inline uint64_t packetId() const { return at<uint64_t>(ZT_PACKET_IDX_IV); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Value of link quality counter extracted from this packet's ID, range 0 to 7 (3 bits)
|
||||||
|
*/
|
||||||
|
inline unsigned int linkQualityCounter() const { return (unsigned int)(reinterpret_cast<const uint8_t *>(data())[7] & 7); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set packet verb
|
* Set packet verb
|
||||||
*
|
*
|
||||||
@@ -1302,8 +1354,9 @@ public:
|
|||||||
*
|
*
|
||||||
* @param key 32-byte key
|
* @param key 32-byte key
|
||||||
* @param encryptPayload If true, encrypt packet payload, else just MAC
|
* @param encryptPayload If true, encrypt packet payload, else just MAC
|
||||||
|
* @param counter Packet send counter for destination peer -- only least significant 3 bits are used
|
||||||
*/
|
*/
|
||||||
void armor(const void *key,bool encryptPayload);
|
void armor(const void *key,bool encryptPayload,unsigned int counter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify and (if encrypted) decrypt packet
|
* Verify and (if encrypted) decrypt packet
|
||||||
@@ -1317,6 +1370,27 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool dearmor(const void *key);
|
bool dearmor(const void *key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt/decrypt a separately armored portion of a packet
|
||||||
|
*
|
||||||
|
* This currently uses Salsa20/12, but any message that uses this should
|
||||||
|
* incorporate a cipher selector to permit this to be changed later. To
|
||||||
|
* ensure that key stream is not reused, the key is slightly altered for
|
||||||
|
* this use case and the same initial 32 keystream bytes that are taken
|
||||||
|
* for MAC in ordinary armor() are also skipped here.
|
||||||
|
*
|
||||||
|
* This is currently only used to mask portions of HELLO as an extra
|
||||||
|
* security precation since most of that message is sent in the clear.
|
||||||
|
*
|
||||||
|
* This must NEVER be used more than once in the same packet, as doing
|
||||||
|
* so will result in re-use of the same key stream.
|
||||||
|
*
|
||||||
|
* @param key 32-byte key
|
||||||
|
* @param start Start of encrypted portion
|
||||||
|
* @param len Length of encrypted portion
|
||||||
|
*/
|
||||||
|
void cryptField(const void *key,unsigned int start,unsigned int len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to compress payload if not already (must be unencrypted)
|
* Attempt to compress payload if not already (must be unencrypted)
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace ZeroTier {
|
|||||||
bool Path::send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
|
bool Path::send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
|
||||||
{
|
{
|
||||||
if (RR->node->putPacket(_localAddress,address(),data,len)) {
|
if (RR->node->putPacket(_localAddress,address(),data,len)) {
|
||||||
sent(now);
|
_lastOut = now;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -21,33 +21,17 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "Constants.hpp"
|
#include "Constants.hpp"
|
||||||
#include "InetAddress.hpp"
|
#include "InetAddress.hpp"
|
||||||
|
#include "SharedPtr.hpp"
|
||||||
// Note: if you change these flags check the logic below. Some of it depends
|
#include "AtomicCounter.hpp"
|
||||||
// on these bits being what they are.
|
#include "NonCopyable.hpp"
|
||||||
|
#include "Utils.hpp"
|
||||||
/**
|
|
||||||
* Flag indicating that this path is suboptimal
|
|
||||||
*
|
|
||||||
* Clusters set this flag on remote paths if GeoIP or other routing decisions
|
|
||||||
* indicate that a peer should be handed off to another cluster member.
|
|
||||||
*/
|
|
||||||
#define ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL 0x0001
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag indicating that this path is optimal
|
|
||||||
*
|
|
||||||
* Peers set this flag on paths that are pushed by a cluster and indicated as
|
|
||||||
* optimal. A second flag is needed since we want to prioritize cluster optimal
|
|
||||||
* paths and de-prioritize sub-optimal paths and for new paths we don't know
|
|
||||||
* which one they are. So we want a trinary state: optimal, suboptimal, unknown.
|
|
||||||
*/
|
|
||||||
#define ZT_PATH_FLAG_CLUSTER_OPTIMAL 0x0002
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum return value of preferenceRank()
|
* Maximum return value of preferenceRank()
|
||||||
@@ -59,89 +43,147 @@ namespace ZeroTier {
|
|||||||
class RuntimeEnvironment;
|
class RuntimeEnvironment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for paths
|
* A path across the physical network
|
||||||
*
|
|
||||||
* The base Path class is an immutable value.
|
|
||||||
*/
|
*/
|
||||||
class Path
|
class Path : NonCopyable
|
||||||
|
{
|
||||||
|
friend class SharedPtr<Path>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Efficient unique key for paths in a Hashtable
|
||||||
|
*/
|
||||||
|
class HashKey
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
HashKey() {}
|
||||||
|
|
||||||
|
HashKey(const InetAddress &l,const InetAddress &r)
|
||||||
|
{
|
||||||
|
// This is an ad-hoc bit packing algorithm to yield unique keys for
|
||||||
|
// remote addresses and their local-side counterparts if defined.
|
||||||
|
// Portability across runtimes is not needed.
|
||||||
|
if (r.ss_family == AF_INET) {
|
||||||
|
_k[0] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_addr.s_addr;
|
||||||
|
_k[1] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port;
|
||||||
|
if (l.ss_family == AF_INET) {
|
||||||
|
_k[2] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&l)->sin_addr.s_addr;
|
||||||
|
_k[3] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port;
|
||||||
|
} else {
|
||||||
|
_k[2] = 0;
|
||||||
|
_k[3] = 0;
|
||||||
|
}
|
||||||
|
} else if (r.ss_family == AF_INET6) {
|
||||||
|
const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr);
|
||||||
|
uint8_t *b = reinterpret_cast<uint8_t *>(_k);
|
||||||
|
for(unsigned int i=0;i<16;++i) b[i] = a[i];
|
||||||
|
_k[2] = ~((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port);
|
||||||
|
if (l.ss_family == AF_INET6) {
|
||||||
|
_k[2] ^= ((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port) << 32;
|
||||||
|
a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&l)->sin6_addr.s6_addr);
|
||||||
|
b += 24;
|
||||||
|
for(unsigned int i=0;i<8;++i) b[i] = a[i];
|
||||||
|
a += 8;
|
||||||
|
for(unsigned int i=0;i<8;++i) b[i] ^= a[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_k[0] = 0;
|
||||||
|
_k[1] = 0;
|
||||||
|
_k[2] = 0;
|
||||||
|
_k[3] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned long hashCode() const { return (unsigned long)(_k[0] + _k[1] + _k[2] + _k[3]); }
|
||||||
|
|
||||||
|
inline bool operator==(const HashKey &k) const { return ( (_k[0] == k._k[0]) && (_k[1] == k._k[1]) && (_k[2] == k._k[2]) && (_k[3] == k._k[3]) ); }
|
||||||
|
inline bool operator!=(const HashKey &k) const { return (!(*this == k)); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64_t _k[4];
|
||||||
|
};
|
||||||
|
|
||||||
Path() :
|
Path() :
|
||||||
_lastSend(0),
|
_lastOut(0),
|
||||||
_lastPing(0),
|
_lastIn(0),
|
||||||
_lastKeepalive(0),
|
_lastTrustEstablishedPacketReceived(0),
|
||||||
_lastReceived(0),
|
_incomingLinkQualityFastLog(0xffffffffffffffffULL),
|
||||||
|
_incomingLinkQualitySlowLogPtr(0),
|
||||||
|
_incomingLinkQualitySlowLogCounter(-64), // discard first fast log
|
||||||
|
_incomingLinkQualityPreviousPacketCounter(0),
|
||||||
|
_outgoingPacketCounter(0),
|
||||||
_addr(),
|
_addr(),
|
||||||
_localAddress(),
|
_localAddress(),
|
||||||
_flags(0),
|
|
||||||
_ipScope(InetAddress::IP_SCOPE_NONE)
|
_ipScope(InetAddress::IP_SCOPE_NONE)
|
||||||
{
|
{
|
||||||
|
for(int i=0;i<(int)sizeof(_incomingLinkQualitySlowLog);++i)
|
||||||
|
_incomingLinkQualitySlowLog[i] = ZT_PATH_LINK_QUALITY_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
Path(const InetAddress &localAddress,const InetAddress &addr) :
|
Path(const InetAddress &localAddress,const InetAddress &addr) :
|
||||||
_lastSend(0),
|
_lastOut(0),
|
||||||
_lastPing(0),
|
_lastIn(0),
|
||||||
_lastKeepalive(0),
|
_lastTrustEstablishedPacketReceived(0),
|
||||||
_lastReceived(0),
|
_incomingLinkQualityFastLog(0xffffffffffffffffULL),
|
||||||
|
_incomingLinkQualitySlowLogPtr(0),
|
||||||
|
_incomingLinkQualitySlowLogCounter(-64), // discard first fast log
|
||||||
|
_incomingLinkQualityPreviousPacketCounter(0),
|
||||||
|
_outgoingPacketCounter(0),
|
||||||
_addr(addr),
|
_addr(addr),
|
||||||
_localAddress(localAddress),
|
_localAddress(localAddress),
|
||||||
_flags(0),
|
|
||||||
_ipScope(addr.ipScope())
|
_ipScope(addr.ipScope())
|
||||||
{
|
{
|
||||||
}
|
for(int i=0;i<(int)sizeof(_incomingLinkQualitySlowLog);++i)
|
||||||
|
_incomingLinkQualitySlowLog[i] = ZT_PATH_LINK_QUALITY_MAX;
|
||||||
inline Path &operator=(const Path &p)
|
|
||||||
{
|
|
||||||
if (this != &p)
|
|
||||||
memcpy(this,&p,sizeof(Path));
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a packet is sent to this remote path
|
* Called when a packet is received from this remote path, regardless of content
|
||||||
*
|
|
||||||
* This is called automatically by Path::send().
|
|
||||||
*
|
|
||||||
* @param t Time of send
|
|
||||||
*/
|
|
||||||
inline void sent(uint64_t t) { _lastSend = t; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when we've sent a ping or echo
|
|
||||||
*
|
|
||||||
* @param t Time of send
|
|
||||||
*/
|
|
||||||
inline void pinged(uint64_t t) { _lastPing = t; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when we send a NAT keepalive
|
|
||||||
*
|
|
||||||
* @param t Time of send
|
|
||||||
*/
|
|
||||||
inline void sentKeepalive(uint64_t t) { _lastKeepalive = t; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a packet is received from this remote path
|
|
||||||
*
|
*
|
||||||
* @param t Time of receive
|
* @param t Time of receive
|
||||||
*/
|
*/
|
||||||
inline void received(uint64_t t)
|
inline void received(const uint64_t t) { _lastIn = t; }
|
||||||
{
|
|
||||||
_lastReceived = t;
|
|
||||||
_probation = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param now Current time
|
* Update link quality using a counter from an incoming packet (or packet head in fragmented case)
|
||||||
* @return True if this path appears active
|
*
|
||||||
|
* @param counter Packet link quality counter (range 0 to 7, must not have other bits set)
|
||||||
*/
|
*/
|
||||||
inline bool active(uint64_t now) const
|
inline void updateLinkQuality(const unsigned int counter)
|
||||||
{
|
{
|
||||||
return ( ((now - _lastReceived) < ZT_PATH_ACTIVITY_TIMEOUT) && (_probation < ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION) );
|
const unsigned int prev = _incomingLinkQualityPreviousPacketCounter;
|
||||||
|
_incomingLinkQualityPreviousPacketCounter = counter;
|
||||||
|
const uint64_t fl = (_incomingLinkQualityFastLog = ((_incomingLinkQualityFastLog << 1) | (uint64_t)(prev == ((counter - 1) & 0x7))));
|
||||||
|
if (++_incomingLinkQualitySlowLogCounter >= 64) {
|
||||||
|
_incomingLinkQualitySlowLogCounter = 0;
|
||||||
|
_incomingLinkQualitySlowLog[_incomingLinkQualitySlowLogPtr++ % sizeof(_incomingLinkQualitySlowLog)] = Utils::countBits(fl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a packet via this path
|
* @return Link quality from 0 (min) to 255 (max)
|
||||||
|
*/
|
||||||
|
inline unsigned int linkQuality() const
|
||||||
|
{
|
||||||
|
unsigned long slsize = _incomingLinkQualitySlowLogPtr;
|
||||||
|
if (slsize > (unsigned long)sizeof(_incomingLinkQualitySlowLog))
|
||||||
|
slsize = (unsigned long)sizeof(_incomingLinkQualitySlowLog);
|
||||||
|
else if (!slsize)
|
||||||
|
return 255; // ZT_PATH_LINK_QUALITY_MAX
|
||||||
|
unsigned long lq = 0;
|
||||||
|
for(unsigned long i=0;i<slsize;++i)
|
||||||
|
lq += (unsigned long)_incomingLinkQualitySlowLog[i] * 4;
|
||||||
|
lq /= slsize;
|
||||||
|
return (unsigned int)((lq >= 255) ? 255 : lq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set time last trusted packet was received (done in Peer::received())
|
||||||
|
*/
|
||||||
|
inline void trustedPacketReceived(const uint64_t t) { _lastTrustEstablishedPacketReceived = t; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a packet via this path (last out time is also updated)
|
||||||
*
|
*
|
||||||
* @param RR Runtime environment
|
* @param RR Runtime environment
|
||||||
* @param data Packet data
|
* @param data Packet data
|
||||||
@@ -151,117 +193,43 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now);
|
bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manually update last sent time
|
||||||
|
*
|
||||||
|
* @param t Time of send
|
||||||
|
*/
|
||||||
|
inline void sent(const uint64_t t) { _lastOut = t; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Address of local side of this path or NULL if unspecified
|
* @return Address of local side of this path or NULL if unspecified
|
||||||
*/
|
*/
|
||||||
inline const InetAddress &localAddress() const throw() { return _localAddress; }
|
inline const InetAddress &localAddress() const { return _localAddress; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Time of last send to this path
|
|
||||||
*/
|
|
||||||
inline uint64_t lastSend() const throw() { return _lastSend; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Time we last pinged or dead path checked this link
|
|
||||||
*/
|
|
||||||
inline uint64_t lastPing() const throw() { return _lastPing; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Time of last keepalive
|
|
||||||
*/
|
|
||||||
inline uint64_t lastKeepalive() const throw() { return _lastKeepalive; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Time of last receive from this path
|
|
||||||
*/
|
|
||||||
inline uint64_t lastReceived() const throw() { return _lastReceived; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Physical address
|
* @return Physical address
|
||||||
*/
|
*/
|
||||||
inline const InetAddress &address() const throw() { return _addr; }
|
inline const InetAddress &address() const { return _addr; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return IP scope -- faster shortcut for address().ipScope()
|
* @return IP scope -- faster shortcut for address().ipScope()
|
||||||
*/
|
*/
|
||||||
inline InetAddress::IpScope ipScope() const throw() { return _ipScope; }
|
inline InetAddress::IpScope ipScope() const { return _ipScope; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param f Valuve of ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL and inverse of ZT_PATH_FLAG_CLUSTER_OPTIMAL (both are changed)
|
* @return True if path has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
|
||||||
*/
|
*/
|
||||||
inline void setClusterSuboptimal(bool f)
|
inline bool trustEstablished(const uint64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Preference rank, higher == better
|
||||||
|
*/
|
||||||
|
inline unsigned int preferenceRank() const
|
||||||
{
|
{
|
||||||
if (f) {
|
// This causes us to rank paths in order of IP scope rank (see InetAdddress.hpp) but
|
||||||
_flags = (_flags | ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) & ~ZT_PATH_FLAG_CLUSTER_OPTIMAL;
|
// within each IP scope class to prefer IPv6 over IPv4.
|
||||||
} else {
|
|
||||||
_flags = (_flags | ZT_PATH_FLAG_CLUSTER_OPTIMAL) & ~ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return True if ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL is set
|
|
||||||
*/
|
|
||||||
inline bool isClusterSuboptimal() const { return ((_flags & ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) != 0); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return True if ZT_PATH_FLAG_CLUSTER_OPTIMAL is set
|
|
||||||
*/
|
|
||||||
inline bool isClusterOptimal() const { return ((_flags & ZT_PATH_FLAG_CLUSTER_OPTIMAL) != 0); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Preference rank, higher == better (will be less than 255)
|
|
||||||
*/
|
|
||||||
inline unsigned int preferenceRank() const throw()
|
|
||||||
{
|
|
||||||
/* First, since the scope enum values in InetAddress.hpp are in order of
|
|
||||||
* use preference rank, we take that. Then we multiple by two, yielding
|
|
||||||
* a sequence like 0, 2, 4, 6, etc. Then if it's IPv6 we add one. This
|
|
||||||
* makes IPv6 addresses of a given scope outrank IPv4 addresses of the
|
|
||||||
* same scope -- e.g. 1 outranks 0. This makes us prefer IPv6, but not
|
|
||||||
* if the address scope/class is of a fundamentally lower rank. */
|
|
||||||
return ( ((unsigned int)_ipScope << 1) | (unsigned int)(_addr.ss_family == AF_INET6) );
|
return ( ((unsigned int)_ipScope << 1) | (unsigned int)(_addr.ss_family == AF_INET6) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return This path's overall quality score (higher is better)
|
|
||||||
*/
|
|
||||||
inline uint64_t score() const throw()
|
|
||||||
{
|
|
||||||
// This is a little bit convoluted because we try to be branch-free, using multiplication instead of branches for boolean flags
|
|
||||||
|
|
||||||
// Start with the last time this path was active, and add a fudge factor to prevent integer underflow if _lastReceived is 0
|
|
||||||
uint64_t score = _lastReceived + (ZT_PEER_DIRECT_PING_DELAY * (ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION + 1));
|
|
||||||
|
|
||||||
// Increase score based on path preference rank, which is based on IP scope and address family
|
|
||||||
score += preferenceRank() * (ZT_PEER_DIRECT_PING_DELAY / ZT_PATH_MAX_PREFERENCE_RANK);
|
|
||||||
|
|
||||||
// Increase score if this is known to be an optimal path to a cluster
|
|
||||||
score += (uint64_t)(_flags & ZT_PATH_FLAG_CLUSTER_OPTIMAL) * (ZT_PEER_DIRECT_PING_DELAY / 2); // /2 because CLUSTER_OPTIMAL is flag 0x0002
|
|
||||||
|
|
||||||
// Decrease score if this is known to be a sub-optimal path to a cluster
|
|
||||||
score -= (uint64_t)(_flags & ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) * ZT_PEER_DIRECT_PING_DELAY;
|
|
||||||
|
|
||||||
// Penalize for missed ECHO tests in dead path detection
|
|
||||||
score -= (uint64_t)((ZT_PEER_DIRECT_PING_DELAY / 2) * _probation);
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return True if path is considered reliable (no NAT keepalives etc. are needed)
|
|
||||||
*/
|
|
||||||
inline bool reliable() const throw()
|
|
||||||
{
|
|
||||||
if ((_addr.ss_family == AF_INET)||(_addr.ss_family == AF_INET6))
|
|
||||||
return ((_ipScope != InetAddress::IP_SCOPE_GLOBAL)&&(_ipScope != InetAddress::IP_SCOPE_PSEUDOPRIVATE));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return True if address is non-NULL
|
|
||||||
*/
|
|
||||||
inline operator bool() const throw() { return (_addr); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether this address is valid for a ZeroTier path
|
* Check whether this address is valid for a ZeroTier path
|
||||||
*
|
*
|
||||||
@@ -272,7 +240,6 @@ public:
|
|||||||
* @return True if address is good for ZeroTier path use
|
* @return True if address is good for ZeroTier path use
|
||||||
*/
|
*/
|
||||||
static inline bool isAddressValidForPath(const InetAddress &a)
|
static inline bool isAddressValidForPath(const InetAddress &a)
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) {
|
if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) {
|
||||||
switch(a.ipScope()) {
|
switch(a.ipScope()) {
|
||||||
@@ -304,60 +271,46 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Current path probation count (for dead path detect)
|
* @return True if path appears alive
|
||||||
*/
|
*/
|
||||||
inline unsigned int probation() const { return _probation; }
|
inline bool alive(const uint64_t now) const { return ((now - _lastIn) <= ZT_PATH_ALIVE_TIMEOUT); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increase this path's probation violation count (for dead path detect)
|
* @return True if this path needs a heartbeat
|
||||||
*/
|
*/
|
||||||
inline void increaseProbation() { ++_probation; }
|
inline bool needsHeartbeat(const uint64_t now) const { return ((now - _lastOut) >= ZT_PATH_HEARTBEAT_PERIOD); }
|
||||||
|
|
||||||
template<unsigned int C>
|
/**
|
||||||
inline void serialize(Buffer<C> &b) const
|
* @return Last time we sent something
|
||||||
{
|
*/
|
||||||
b.append((uint8_t)2); // version
|
inline uint64_t lastOut() const { return _lastOut; }
|
||||||
b.append((uint64_t)_lastSend);
|
|
||||||
b.append((uint64_t)_lastPing);
|
|
||||||
b.append((uint64_t)_lastKeepalive);
|
|
||||||
b.append((uint64_t)_lastReceived);
|
|
||||||
_addr.serialize(b);
|
|
||||||
_localAddress.serialize(b);
|
|
||||||
b.append((uint16_t)_flags);
|
|
||||||
b.append((uint16_t)_probation);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<unsigned int C>
|
/**
|
||||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
* @return Last time we received anything
|
||||||
{
|
*/
|
||||||
unsigned int p = startAt;
|
inline uint64_t lastIn() const { return _lastIn; }
|
||||||
if (b[p++] != 2)
|
|
||||||
throw std::invalid_argument("invalid serialized Path");
|
|
||||||
_lastSend = b.template at<uint64_t>(p); p += 8;
|
|
||||||
_lastPing = b.template at<uint64_t>(p); p += 8;
|
|
||||||
_lastKeepalive = b.template at<uint64_t>(p); p += 8;
|
|
||||||
_lastReceived = b.template at<uint64_t>(p); p += 8;
|
|
||||||
p += _addr.deserialize(b,p);
|
|
||||||
p += _localAddress.deserialize(b,p);
|
|
||||||
_flags = b.template at<uint16_t>(p); p += 2;
|
|
||||||
_probation = b.template at<uint16_t>(p); p += 2;
|
|
||||||
_ipScope = _addr.ipScope();
|
|
||||||
return (p - startAt);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator==(const Path &p) const { return ((p._addr == _addr)&&(p._localAddress == _localAddress)); }
|
/**
|
||||||
inline bool operator!=(const Path &p) const { return ((p._addr != _addr)||(p._localAddress != _localAddress)); }
|
* Return and increment outgoing packet counter (used with Packet::armor())
|
||||||
|
*
|
||||||
|
* @return Next value that should be used for outgoing packet counter (only least significant 3 bits are used)
|
||||||
|
*/
|
||||||
|
inline unsigned int nextOutgoingCounter() { return _outgoingPacketCounter++; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t _lastSend;
|
volatile uint64_t _lastOut;
|
||||||
uint64_t _lastPing;
|
volatile uint64_t _lastIn;
|
||||||
uint64_t _lastKeepalive;
|
volatile uint64_t _lastTrustEstablishedPacketReceived;
|
||||||
uint64_t _lastReceived;
|
volatile uint64_t _incomingLinkQualityFastLog;
|
||||||
|
volatile unsigned long _incomingLinkQualitySlowLogPtr;
|
||||||
|
volatile signed int _incomingLinkQualitySlowLogCounter;
|
||||||
|
volatile unsigned int _incomingLinkQualityPreviousPacketCounter;
|
||||||
|
volatile unsigned int _outgoingPacketCounter;
|
||||||
InetAddress _addr;
|
InetAddress _addr;
|
||||||
InetAddress _localAddress;
|
InetAddress _localAddress;
|
||||||
unsigned int _flags;
|
|
||||||
unsigned int _probation;
|
|
||||||
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
|
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
|
||||||
|
volatile uint8_t _incomingLinkQualitySlowLog[32];
|
||||||
|
AtomicCounter __refCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -27,25 +27,31 @@
|
|||||||
#include "Cluster.hpp"
|
#include "Cluster.hpp"
|
||||||
#include "Packet.hpp"
|
#include "Packet.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#ifndef AF_MAX
|
||||||
|
#if AF_INET > AF_INET6
|
||||||
#define ZT_PEER_PATH_SORT_INTERVAL 5000
|
#define AF_MAX AF_INET
|
||||||
|
#else
|
||||||
|
#define AF_MAX AF_INET6
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
// Used to send varying values for NAT keepalive
|
|
||||||
static uint32_t _natKeepaliveBuf = 0;
|
|
||||||
|
|
||||||
Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) :
|
Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) :
|
||||||
RR(renv),
|
RR(renv),
|
||||||
_lastUsed(0),
|
|
||||||
_lastReceive(0),
|
_lastReceive(0),
|
||||||
_lastUnicastFrame(0),
|
_lastNontrivialReceive(0),
|
||||||
_lastMulticastFrame(0),
|
_lastTriedMemorizedPath(0),
|
||||||
_lastAnnouncedTo(0),
|
|
||||||
_lastDirectPathPushSent(0),
|
_lastDirectPathPushSent(0),
|
||||||
_lastDirectPathPushReceive(0),
|
_lastDirectPathPushReceive(0),
|
||||||
_lastPathSort(0),
|
_lastCredentialRequestSent(0),
|
||||||
|
_lastWhoisRequestReceived(0),
|
||||||
|
_lastEchoRequestReceived(0),
|
||||||
|
_lastComRequestReceived(0),
|
||||||
|
_lastComRequestSent(0),
|
||||||
|
_lastCredentialsReceived(0),
|
||||||
|
_lastTrustEstablishedPacketReceived(0),
|
||||||
|
_remoteClusterOptimal4(0),
|
||||||
_vProto(0),
|
_vProto(0),
|
||||||
_vMajor(0),
|
_vMajor(0),
|
||||||
_vMinor(0),
|
_vMinor(0),
|
||||||
@@ -54,29 +60,31 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
|
|||||||
_numPaths(0),
|
_numPaths(0),
|
||||||
_latency(0),
|
_latency(0),
|
||||||
_directPathPushCutoffCount(0),
|
_directPathPushCutoffCount(0),
|
||||||
_networkComs(4),
|
_credentialsCutoffCount(0)
|
||||||
_lastPushedComs(4)
|
|
||||||
{
|
{
|
||||||
|
memset(_remoteClusterOptimal6,0,sizeof(_remoteClusterOptimal6));
|
||||||
if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH))
|
if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH))
|
||||||
throw std::runtime_error("new peer identity key agreement failed");
|
throw std::runtime_error("new peer identity key agreement failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Peer::received(
|
void Peer::received(
|
||||||
const InetAddress &localAddr,
|
const SharedPtr<Path> &path,
|
||||||
const InetAddress &remoteAddr,
|
const unsigned int hops,
|
||||||
unsigned int hops,
|
const uint64_t packetId,
|
||||||
uint64_t packetId,
|
const Packet::Verb verb,
|
||||||
Packet::Verb verb,
|
const uint64_t inRePacketId,
|
||||||
uint64_t inRePacketId,
|
const Packet::Verb inReVerb,
|
||||||
Packet::Verb inReVerb)
|
const bool trustEstablished)
|
||||||
{
|
{
|
||||||
|
const uint64_t now = RR->node->now();
|
||||||
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
bool suboptimalPath = false;
|
bool suboptimalPath = false;
|
||||||
if ((RR->cluster)&&(hops == 0)) {
|
if ((RR->cluster)&&(hops == 0)) {
|
||||||
// Note: findBetterEndpoint() is first since we still want to check
|
// Note: findBetterEndpoint() is first since we still want to check
|
||||||
// for a better endpoint even if we don't actually send a redirect.
|
// for a better endpoint even if we don't actually send a redirect.
|
||||||
InetAddress redirectTo;
|
InetAddress redirectTo;
|
||||||
if ( (verb != Packet::VERB_OK) && (verb != Packet::VERB_ERROR) && (verb != Packet::VERB_RENDEZVOUS) && (verb != Packet::VERB_PUSH_DIRECT_PATHS) && (RR->cluster->findBetterEndpoint(redirectTo,_id.address(),remoteAddr,false)) ) {
|
if ( (verb != Packet::VERB_OK) && (verb != Packet::VERB_ERROR) && (verb != Packet::VERB_RENDEZVOUS) && (verb != Packet::VERB_PUSH_DIRECT_PATHS) && (RR->cluster->findBetterEndpoint(redirectTo,_id.address(),path->address(),false)) ) {
|
||||||
if (_vProto >= 5) {
|
if (_vProto >= 5) {
|
||||||
// For newer peers we can send a more idiomatic verb: PUSH_DIRECT_PATHS.
|
// For newer peers we can send a more idiomatic verb: PUSH_DIRECT_PATHS.
|
||||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
|
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
|
||||||
@@ -93,8 +101,8 @@ void Peer::received(
|
|||||||
outp.append(redirectTo.rawIpData(),16);
|
outp.append(redirectTo.rawIpData(),16);
|
||||||
}
|
}
|
||||||
outp.append((uint16_t)redirectTo.port());
|
outp.append((uint16_t)redirectTo.port());
|
||||||
outp.armor(_key,true);
|
outp.armor(_key,true,path->nextOutgoingCounter());
|
||||||
RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
|
path->send(RR,outp.data(),outp.size(),now);
|
||||||
} else {
|
} else {
|
||||||
// For older peers we use RENDEZVOUS to coax them into contacting us elsewhere.
|
// For older peers we use RENDEZVOUS to coax them into contacting us elsewhere.
|
||||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
||||||
@@ -108,162 +116,116 @@ void Peer::received(
|
|||||||
outp.append((uint8_t)16);
|
outp.append((uint8_t)16);
|
||||||
outp.append(redirectTo.rawIpData(),16);
|
outp.append(redirectTo.rawIpData(),16);
|
||||||
}
|
}
|
||||||
outp.armor(_key,true);
|
outp.armor(_key,true,path->nextOutgoingCounter());
|
||||||
RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
|
path->send(RR,outp.data(),outp.size(),now);
|
||||||
}
|
}
|
||||||
suboptimalPath = true;
|
suboptimalPath = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const uint64_t now = RR->node->now();
|
|
||||||
_lastReceive = now;
|
_lastReceive = now;
|
||||||
if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME))
|
switch (verb) {
|
||||||
_lastUnicastFrame = now;
|
case Packet::VERB_FRAME:
|
||||||
else if (verb == Packet::VERB_MULTICAST_FRAME)
|
case Packet::VERB_EXT_FRAME:
|
||||||
_lastMulticastFrame = now;
|
case Packet::VERB_NETWORK_CONFIG_REQUEST:
|
||||||
|
case Packet::VERB_NETWORK_CONFIG:
|
||||||
|
case Packet::VERB_MULTICAST_FRAME:
|
||||||
|
_lastNontrivialReceive = now;
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trustEstablished) {
|
||||||
|
_lastTrustEstablishedPacketReceived = now;
|
||||||
|
path->trustedPacketReceived(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_vProto >= 9)
|
||||||
|
path->updateLinkQuality((unsigned int)(packetId & 7));
|
||||||
|
|
||||||
if (hops == 0) {
|
if (hops == 0) {
|
||||||
bool pathIsConfirmed = false;
|
bool pathIsConfirmed = false;
|
||||||
unsigned int np = _numPaths;
|
{
|
||||||
for(unsigned int p=0;p<np;++p) {
|
Mutex::Lock _l(_paths_m);
|
||||||
if ((_paths[p].address() == remoteAddr)&&(_paths[p].localAddress() == localAddr)) {
|
for(unsigned int p=0;p<_numPaths;++p) {
|
||||||
_paths[p].received(now);
|
if (_paths[p].path->address() == path->address()) {
|
||||||
|
_paths[p].lastReceive = now;
|
||||||
|
_paths[p].path = path; // local address may have changed!
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
_paths[p].setClusterSuboptimal(suboptimalPath);
|
_paths[p].localClusterSuboptimal = suboptimalPath;
|
||||||
#endif
|
#endif
|
||||||
pathIsConfirmed = true;
|
pathIsConfirmed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((!pathIsConfirmed)&&(RR->node->shouldUsePathForZeroTierTraffic(localAddr,remoteAddr))) {
|
if ( (!pathIsConfirmed) && (RR->node->shouldUsePathForZeroTierTraffic(_id.address(),path->localAddress(),path->address())) ) {
|
||||||
if (verb == Packet::VERB_OK) {
|
if (verb == Packet::VERB_OK) {
|
||||||
|
Mutex::Lock _l(_paths_m);
|
||||||
|
|
||||||
Path *slot = (Path *)0;
|
// Since this is a new path, figure out where to put it (possibly replacing an old/dead one)
|
||||||
if (np < ZT_MAX_PEER_NETWORK_PATHS) {
|
unsigned int slot;
|
||||||
slot = &(_paths[np++]);
|
if (_numPaths < ZT_MAX_PEER_NETWORK_PATHS) {
|
||||||
|
slot = _numPaths++;
|
||||||
} else {
|
} else {
|
||||||
uint64_t slotWorstScore = 0xffffffffffffffffULL;
|
// First try to replace the worst within the same address family, if possible
|
||||||
for(unsigned int p=0;p<ZT_MAX_PEER_NETWORK_PATHS;++p) {
|
int worstSlot = -1;
|
||||||
if (!_paths[p].active(now)) {
|
uint64_t worstScore = 0xffffffffffffffffULL;
|
||||||
slot = &(_paths[p]);
|
for(unsigned int p=0;p<_numPaths;++p) {
|
||||||
break;
|
if (_paths[p].path->address().ss_family == path->address().ss_family) {
|
||||||
|
const uint64_t s = _pathScore(p,now);
|
||||||
|
if (s < worstScore) {
|
||||||
|
worstScore = s;
|
||||||
|
worstSlot = (int)p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (worstSlot >= 0) {
|
||||||
|
slot = (unsigned int)worstSlot;
|
||||||
} else {
|
} else {
|
||||||
const uint64_t score = _paths[p].score();
|
// If we can't find one with the same family, replace the worst of any family
|
||||||
if (score <= slotWorstScore) {
|
slot = ZT_MAX_PEER_NETWORK_PATHS - 1;
|
||||||
slotWorstScore = score;
|
for(unsigned int p=0;p<_numPaths;++p) {
|
||||||
slot = &(_paths[p]);
|
const uint64_t s = _pathScore(p,now);
|
||||||
|
if (s < worstScore) {
|
||||||
|
worstScore = s;
|
||||||
|
slot = p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (slot) {
|
|
||||||
*slot = Path(localAddr,remoteAddr);
|
|
||||||
slot->received(now);
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
|
||||||
slot->setClusterSuboptimal(suboptimalPath);
|
|
||||||
#endif
|
|
||||||
_numPaths = np;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
_paths[slot].lastReceive = now;
|
||||||
|
_paths[slot].path = path;
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
_paths[slot].localClusterSuboptimal = suboptimalPath;
|
||||||
if (RR->cluster)
|
if (RR->cluster)
|
||||||
RR->cluster->broadcastHavePeer(_id);
|
RR->cluster->broadcastHavePeer(_id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),path->address().toString().c_str());
|
||||||
TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),remoteAddr.toString().c_str());
|
attemptToContactAt(path->localAddress(),path->address(),now,true,path->nextOutgoingCounter());
|
||||||
|
path->sent(now);
|
||||||
if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
|
|
||||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
|
|
||||||
outp.armor(_key,true);
|
|
||||||
RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
|
|
||||||
} else {
|
|
||||||
sendHELLO(localAddr,remoteAddr,now);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (this->trustEstablished(now)) {
|
||||||
|
// Send PUSH_DIRECT_PATHS if hops>0 (relayed) and we have a trust relationship (common network membership)
|
||||||
if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) {
|
|
||||||
_lastAnnouncedTo = now;
|
|
||||||
const std::vector< SharedPtr<Network> > networks(RR->node->allNetworks());
|
|
||||||
for(std::vector< SharedPtr<Network> >::const_iterator n(networks.begin());n!=networks.end();++n)
|
|
||||||
(*n)->tryAnnounceMulticastGroupsTo(SharedPtr<Peer>(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl)
|
|
||||||
{
|
|
||||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
|
|
||||||
outp.append((unsigned char)ZT_PROTO_VERSION);
|
|
||||||
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
|
|
||||||
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
|
|
||||||
outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
|
|
||||||
outp.append(now);
|
|
||||||
RR->identity.serialize(outp,false);
|
|
||||||
atAddress.serialize(outp);
|
|
||||||
outp.append((uint64_t)RR->topology->worldId());
|
|
||||||
outp.append((uint64_t)RR->topology->worldTimestamp());
|
|
||||||
|
|
||||||
outp.armor(_key,false); // HELLO is sent in the clear
|
|
||||||
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size(),ttl);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
|
|
||||||
{
|
|
||||||
Path *p = (Path *)0;
|
|
||||||
|
|
||||||
if (inetAddressFamily != 0) {
|
|
||||||
p = _getBestPath(now,inetAddressFamily);
|
|
||||||
} else {
|
|
||||||
p = _getBestPath(now);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p) {
|
|
||||||
if ((now - p->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) {
|
|
||||||
//TRACE("PING %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived());
|
|
||||||
sendHELLO(p->localAddress(),p->address(),now);
|
|
||||||
p->sent(now);
|
|
||||||
p->pinged(now);
|
|
||||||
} else if ( ((now - std::max(p->lastSend(),p->lastKeepalive())) >= ZT_NAT_KEEPALIVE_DELAY) && (!p->reliable()) ) {
|
|
||||||
//TRACE("NAT keepalive %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived());
|
|
||||||
_natKeepaliveBuf += (uint32_t)((now * 0x9e3779b1) >> 1); // tumble this around to send constantly varying (meaningless) payloads
|
|
||||||
RR->node->putPacket(p->localAddress(),p->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf));
|
|
||||||
p->sentKeepalive(now);
|
|
||||||
} else {
|
|
||||||
//TRACE("no PING or NAT keepalive: addr==%s reliable==%d %llums/%llums send/receive inactivity",p->address().toString().c_str(),(int)p->reliable(),now - p->lastSend(),now - p->lastReceived());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Peer::pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAddress,uint64_t now,bool force,bool includePrivatePaths)
|
|
||||||
{
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
// Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
|
// Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
|
||||||
if (RR->cluster)
|
const bool haveCluster = (RR->cluster);
|
||||||
return false;
|
#else
|
||||||
|
const bool haveCluster = false;
|
||||||
#endif
|
#endif
|
||||||
|
if ( ((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL) && (!haveCluster) ) {
|
||||||
if (!force) {
|
_lastDirectPathPushSent = now;
|
||||||
if ((now - _lastDirectPathPushSent) < ZT_DIRECT_PATH_PUSH_INTERVAL)
|
|
||||||
return false;
|
|
||||||
else _lastDirectPathPushSent = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<InetAddress> pathsToPush;
|
std::vector<InetAddress> pathsToPush;
|
||||||
|
|
||||||
std::vector<InetAddress> dps(RR->node->directPaths());
|
std::vector<InetAddress> dps(RR->node->directPaths());
|
||||||
for(std::vector<InetAddress>::const_iterator i(dps.begin());i!=dps.end();++i) {
|
for(std::vector<InetAddress>::const_iterator i(dps.begin());i!=dps.end();++i)
|
||||||
if ((includePrivatePaths)||(i->ipScope() == InetAddress::IP_SCOPE_GLOBAL))
|
|
||||||
pathsToPush.push_back(*i);
|
pathsToPush.push_back(*i);
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
|
std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
|
||||||
for(unsigned long i=0,added=0;i<sym.size();++i) {
|
for(unsigned long i=0,added=0;i<sym.size();++i) {
|
||||||
@@ -274,11 +236,9 @@ bool Peer::pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAdd
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pathsToPush.empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
if (pathsToPush.size() > 0) {
|
||||||
#ifdef ZT_TRACE
|
#ifdef ZT_TRACE
|
||||||
{
|
|
||||||
std::string ps;
|
std::string ps;
|
||||||
for(std::vector<InetAddress>::const_iterator p(pathsToPush.begin());p!=pathsToPush.end();++p) {
|
for(std::vector<InetAddress>::const_iterator p(pathsToPush.begin());p!=pathsToPush.end();++p) {
|
||||||
if (ps.length() > 0)
|
if (ps.length() > 0)
|
||||||
@@ -286,7 +246,6 @@ bool Peer::pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAdd
|
|||||||
ps.append(p->toString());
|
ps.append(p->toString());
|
||||||
}
|
}
|
||||||
TRACE("pushing %u direct paths to %s: %s",(unsigned int)pathsToPush.size(),_id.address().toString().c_str(),ps.c_str());
|
TRACE("pushing %u direct paths to %s: %s",(unsigned int)pathsToPush.size(),_id.address().toString().c_str(),ps.c_str());
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
|
std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
|
||||||
@@ -321,238 +280,218 @@ bool Peer::pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAdd
|
|||||||
|
|
||||||
if (count) {
|
if (count) {
|
||||||
outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
|
outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
|
||||||
outp.armor(_key,true);
|
outp.armor(_key,true,path->nextOutgoingCounter());
|
||||||
RR->node->putPacket(localAddr,toAddress,outp.data(),outp.size(),0);
|
path->send(RR,outp.data(),outp.size(),now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Peer::hasActivePathTo(uint64_t now,const InetAddress &addr) const
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_paths_m);
|
||||||
|
for(unsigned int p=0;p<_numPaths;++p) {
|
||||||
|
if ( (_paths[p].path->address() == addr) && ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && (_paths[p].path->alive(now)) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Peer::resetWithinScope(InetAddress::IpScope scope,uint64_t now)
|
|
||||||
{
|
|
||||||
unsigned int np = _numPaths;
|
|
||||||
unsigned int x = 0;
|
|
||||||
unsigned int y = 0;
|
|
||||||
while (x < np) {
|
|
||||||
if (_paths[x].address().ipScope() == scope) {
|
|
||||||
// Resetting a path means sending a HELLO and then forgetting it. If we
|
|
||||||
// get OK(HELLO) then it will be re-learned.
|
|
||||||
sendHELLO(_paths[x].localAddress(),_paths[x].address(),now);
|
|
||||||
} else {
|
|
||||||
_paths[y++] = _paths[x];
|
|
||||||
}
|
|
||||||
++x;
|
|
||||||
}
|
|
||||||
_numPaths = y;
|
|
||||||
return (y < np);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Peer::getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const
|
|
||||||
{
|
|
||||||
uint64_t bestV4 = 0,bestV6 = 0;
|
|
||||||
for(unsigned int p=0,np=_numPaths;p<np;++p) {
|
|
||||||
if (_paths[p].active(now)) {
|
|
||||||
uint64_t lr = _paths[p].lastReceived();
|
|
||||||
if (lr) {
|
|
||||||
if (_paths[p].address().isV4()) {
|
|
||||||
if (lr >= bestV4) {
|
|
||||||
bestV4 = lr;
|
|
||||||
v4 = _paths[p].address();
|
|
||||||
}
|
|
||||||
} else if (_paths[p].address().isV6()) {
|
|
||||||
if (lr >= bestV6) {
|
|
||||||
bestV6 = lr;
|
|
||||||
v6 = _paths[p].address();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Peer::networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfMembership &com) const
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_networkComs_m);
|
|
||||||
const _NetworkCom *ourCom = _networkComs.get(nwid);
|
|
||||||
if (ourCom)
|
|
||||||
return ourCom->com.agreesWith(com);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Peer::validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com)
|
bool Peer::sendDirect(const void *data,unsigned int len,uint64_t now,bool forceEvenIfDead)
|
||||||
{
|
{
|
||||||
// Sanity checks
|
Mutex::Lock _l(_paths_m);
|
||||||
if ((!com)||(com.issuedTo() != _id.address()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Return true if we already have this *exact* COM
|
int bestp = -1;
|
||||||
{
|
uint64_t best = 0ULL;
|
||||||
Mutex::Lock _l(_networkComs_m);
|
for(unsigned int p=0;p<_numPaths;++p) {
|
||||||
_NetworkCom *ourCom = _networkComs.get(nwid);
|
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && (_paths[p].path->alive(now)||(forceEvenIfDead)) ) {
|
||||||
if ((ourCom)&&(ourCom->com == com))
|
const uint64_t s = _pathScore(p,now);
|
||||||
return true;
|
if (s >= best) {
|
||||||
}
|
best = s;
|
||||||
|
bestp = (int)p;
|
||||||
// Check signature, log and return if cert is invalid
|
}
|
||||||
if (com.signedBy() != Network::controllerFor(nwid)) {
|
}
|
||||||
TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)nwid,com.signedBy().toString().c_str());
|
|
||||||
return false; // invalid signer
|
|
||||||
}
|
|
||||||
|
|
||||||
if (com.signedBy() == RR->identity.address()) {
|
|
||||||
|
|
||||||
// We are the controller: RR->identity.address() == controller() == cert.signedBy()
|
|
||||||
// So, verify that we signed th cert ourself
|
|
||||||
if (!com.verify(RR->identity)) {
|
|
||||||
TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)nwid,com.signedBy().toString().c_str());
|
|
||||||
return false; // invalid signature
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bestp >= 0) {
|
||||||
|
return _paths[bestp].path->send(RR,data,len,now);
|
||||||
} else {
|
} else {
|
||||||
|
return false;
|
||||||
SharedPtr<Peer> signer(RR->topology->getPeer(com.signedBy()));
|
|
||||||
|
|
||||||
if (!signer) {
|
|
||||||
// This would be rather odd, since this is our controller... could happen
|
|
||||||
// if we get packets before we've gotten config.
|
|
||||||
RR->sw->requestWhois(com.signedBy());
|
|
||||||
return false; // signer unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!com.verify(signer->identity())) {
|
|
||||||
TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)nwid,com.signedBy().toString().c_str());
|
|
||||||
return false; // invalid signature
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we made it past all those checks, add or update cert in our cert info store
|
SharedPtr<Path> Peer::getBestPath(uint64_t now,bool includeExpired)
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_networkComs_m);
|
Mutex::Lock _l(_paths_m);
|
||||||
_networkComs.set(nwid,_NetworkCom(RR->node->now(),com));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
int bestp = -1;
|
||||||
}
|
uint64_t best = 0ULL;
|
||||||
|
for(unsigned int p=0;p<_numPaths;++p) {
|
||||||
bool Peer::needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime)
|
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) || (includeExpired) ) {
|
||||||
{
|
const uint64_t s = _pathScore(p,now);
|
||||||
Mutex::Lock _l(_networkComs_m);
|
if (s >= best) {
|
||||||
uint64_t &lastPushed = _lastPushedComs[nwid];
|
best = s;
|
||||||
const uint64_t tmp = lastPushed;
|
bestp = (int)p;
|
||||||
if (updateLastPushedTime)
|
|
||||||
lastPushed = now;
|
|
||||||
return ((now - tmp) >= (ZT_NETWORK_AUTOCONF_DELAY / 3));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Peer::clean(uint64_t now)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
unsigned int np = _numPaths;
|
|
||||||
unsigned int x = 0;
|
|
||||||
unsigned int y = 0;
|
|
||||||
while (x < np) {
|
|
||||||
if (_paths[x].active(now))
|
|
||||||
_paths[y++] = _paths[x];
|
|
||||||
++x;
|
|
||||||
}
|
|
||||||
_numPaths = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_networkComs_m);
|
|
||||||
{
|
|
||||||
uint64_t *k = (uint64_t *)0;
|
|
||||||
_NetworkCom *v = (_NetworkCom *)0;
|
|
||||||
Hashtable< uint64_t,_NetworkCom >::Iterator i(_networkComs);
|
|
||||||
while (i.next(k,v)) {
|
|
||||||
if ( (!RR->node->belongsToNetwork(*k)) && ((now - v->ts) >= ZT_PEER_NETWORK_COM_EXPIRATION) )
|
|
||||||
_networkComs.erase(*k);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
uint64_t *k = (uint64_t *)0;
|
|
||||||
uint64_t *v = (uint64_t *)0;
|
|
||||||
Hashtable< uint64_t,uint64_t >::Iterator i(_lastPushedComs);
|
|
||||||
while (i.next(k,v)) {
|
|
||||||
if ((now - *v) > (ZT_NETWORK_AUTOCONF_DELAY * 2))
|
|
||||||
_lastPushedComs.erase(*k);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Peer::_doDeadPathDetection(Path &p,const uint64_t now)
|
if (bestp >= 0) {
|
||||||
|
return _paths[bestp].path;
|
||||||
|
} else {
|
||||||
|
return SharedPtr<Path>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter)
|
||||||
{
|
{
|
||||||
/* Dead path detection: if we have sent something to this peer and have not
|
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
|
||||||
* yet received a reply, double check this path. The majority of outbound
|
|
||||||
* packets including Ethernet frames do generate some kind of reply either
|
|
||||||
* immediately or at some point in the near future. This will occasionally
|
|
||||||
* (every NO_ANSWER_TIMEOUT ms) check paths unnecessarily if traffic that
|
|
||||||
* does not generate a response is being sent such as multicast announcements
|
|
||||||
* or frames belonging to unidirectional UDP protocols, but the cost is very
|
|
||||||
* tiny and the benefit in reliability is very large. This takes care of many
|
|
||||||
* failure modes including crap NATs that forget links and spurious changes
|
|
||||||
* to physical network topology that cannot be otherwise detected.
|
|
||||||
*
|
|
||||||
* Each time we do this we increment a probation counter in the path. This
|
|
||||||
* counter is reset on any packet receive over this path. If it reaches the
|
|
||||||
* MAX_PROBATION threshold the path is considred dead. */
|
|
||||||
|
|
||||||
if (
|
outp.append((unsigned char)ZT_PROTO_VERSION);
|
||||||
(p.lastSend() > p.lastReceived()) &&
|
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
|
||||||
((p.lastSend() - p.lastReceived()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
|
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
|
||||||
((now - p.lastPing()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
|
outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
|
||||||
(!p.isClusterSuboptimal()) &&
|
outp.append(now);
|
||||||
(!RR->topology->amRoot())
|
RR->identity.serialize(outp,false);
|
||||||
) {
|
atAddress.serialize(outp);
|
||||||
TRACE("%s(%s) does not seem to be answering in a timely manner, checking if dead (probation == %u)",_id.address().toString().c_str(),p.address().toString().c_str(),p.probation());
|
|
||||||
|
|
||||||
if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
|
outp.append((uint64_t)RR->topology->planetWorldId());
|
||||||
|
outp.append((uint64_t)RR->topology->planetWorldTimestamp());
|
||||||
|
|
||||||
|
const unsigned int startCryptedPortionAt = outp.size();
|
||||||
|
|
||||||
|
std::vector<World> moons(RR->topology->moons());
|
||||||
|
std::vector<uint64_t> moonsWanted(RR->topology->moonsWanted());
|
||||||
|
outp.append((uint16_t)(moons.size() + moonsWanted.size()));
|
||||||
|
for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
|
||||||
|
outp.append((uint8_t)m->type());
|
||||||
|
outp.append((uint64_t)m->id());
|
||||||
|
outp.append((uint64_t)m->timestamp());
|
||||||
|
}
|
||||||
|
for(std::vector<uint64_t>::const_iterator m(moonsWanted.begin());m!=moonsWanted.end();++m) {
|
||||||
|
outp.append((uint8_t)World::TYPE_MOON);
|
||||||
|
outp.append(*m);
|
||||||
|
outp.append((uint64_t)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned int corSizeAt = outp.size();
|
||||||
|
outp.addSize(2);
|
||||||
|
RR->topology->appendCertificateOfRepresentation(outp);
|
||||||
|
outp.setAt(corSizeAt,(uint16_t)(outp.size() - (corSizeAt + 2)));
|
||||||
|
|
||||||
|
outp.cryptField(_key,startCryptedPortionAt,outp.size() - startCryptedPortionAt);
|
||||||
|
|
||||||
|
RR->node->expectReplyTo(outp.packetId());
|
||||||
|
|
||||||
|
if (atAddress) {
|
||||||
|
outp.armor(_key,false,counter); // false == don't encrypt full payload, but add MAC
|
||||||
|
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
|
||||||
|
} else {
|
||||||
|
RR->sw->send(outp,false); // false == don't encrypt full payload, but add MAC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Peer::attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter)
|
||||||
|
{
|
||||||
|
if ( (!sendFullHello) && (_vProto >= 5) && (!((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0))) ) {
|
||||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
|
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
|
||||||
outp.armor(_key,true);
|
RR->node->expectReplyTo(outp.packetId());
|
||||||
p.send(RR,outp.data(),outp.size(),now);
|
outp.armor(_key,true,counter);
|
||||||
p.pinged(now);
|
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
|
||||||
} else {
|
} else {
|
||||||
sendHELLO(p.localAddress(),p.address(),now);
|
sendHELLO(localAddr,atAddress,now,counter);
|
||||||
p.sent(now);
|
|
||||||
p.pinged(now);
|
|
||||||
}
|
|
||||||
|
|
||||||
p.increaseProbation();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Path *Peer::_getBestPath(const uint64_t now)
|
void Peer::tryMemorizedPath(uint64_t now)
|
||||||
{
|
{
|
||||||
Path *bestPath = (Path *)0;
|
if ((now - _lastTriedMemorizedPath) >= ZT_TRY_MEMORIZED_PATH_INTERVAL) {
|
||||||
uint64_t bestPathScore = 0;
|
_lastTriedMemorizedPath = now;
|
||||||
for(unsigned int i=0;i<_numPaths;++i) {
|
InetAddress mp;
|
||||||
const uint64_t score = _paths[i].score();
|
if (RR->node->externalPathLookup(_id.address(),-1,mp))
|
||||||
if ((score >= bestPathScore)&&(_paths[i].active(now))) {
|
attemptToContactAt(InetAddress(),mp,now,true,0);
|
||||||
bestPathScore = score;
|
|
||||||
bestPath = &(_paths[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (bestPath)
|
|
||||||
_doDeadPathDetection(*bestPath,now);
|
|
||||||
return bestPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
Path *Peer::_getBestPath(const uint64_t now,int inetAddressFamily)
|
bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
|
||||||
{
|
{
|
||||||
Path *bestPath = (Path *)0;
|
Mutex::Lock _l(_paths_m);
|
||||||
uint64_t bestPathScore = 0;
|
|
||||||
for(unsigned int i=0;i<_numPaths;++i) {
|
int bestp = -1;
|
||||||
const uint64_t score = _paths[i].score();
|
uint64_t best = 0ULL;
|
||||||
if (((int)_paths[i].address().ss_family == inetAddressFamily)&&(score >= bestPathScore)&&(_paths[i].active(now))) {
|
for(unsigned int p=0;p<_numPaths;++p) {
|
||||||
bestPathScore = score;
|
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && ((inetAddressFamily < 0)||((int)_paths[p].path->address().ss_family == inetAddressFamily)) ) {
|
||||||
bestPath = &(_paths[i]);
|
const uint64_t s = _pathScore(p,now);
|
||||||
|
if (s >= best) {
|
||||||
|
best = s;
|
||||||
|
bestp = (int)p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (bestPath)
|
}
|
||||||
_doDeadPathDetection(*bestPath,now);
|
|
||||||
return bestPath;
|
if (bestp >= 0) {
|
||||||
|
if ( ((now - _paths[bestp].lastReceive) >= ZT_PEER_PING_PERIOD) || (_paths[bestp].path->needsHeartbeat(now)) ) {
|
||||||
|
attemptToContactAt(_paths[bestp].path->localAddress(),_paths[bestp].path->address(),now,false,_paths[bestp].path->nextOutgoingCounter());
|
||||||
|
_paths[bestp].path->sent(now);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Peer::hasActiveDirectPath(uint64_t now) const
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_paths_m);
|
||||||
|
for(unsigned int p=0;p<_numPaths;++p) {
|
||||||
|
if (((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION)&&(_paths[p].path->alive(now)))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Peer::resetWithinScope(InetAddress::IpScope scope,int inetAddressFamily,uint64_t now)
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_paths_m);
|
||||||
|
for(unsigned int p=0;p<_numPaths;++p) {
|
||||||
|
if ( (_paths[p].path->address().ss_family == inetAddressFamily) && (_paths[p].path->address().ipScope() == scope) ) {
|
||||||
|
attemptToContactAt(_paths[p].path->localAddress(),_paths[p].path->address(),now,false,_paths[p].path->nextOutgoingCounter());
|
||||||
|
_paths[p].path->sent(now);
|
||||||
|
_paths[p].lastReceive = 0; // path will not be used unless it speaks again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Peer::getRendezvousAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_paths_m);
|
||||||
|
|
||||||
|
int bestp4 = -1,bestp6 = -1;
|
||||||
|
uint64_t best4 = 0ULL,best6 = 0ULL;
|
||||||
|
for(unsigned int p=0;p<_numPaths;++p) {
|
||||||
|
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && (_paths[p].path->alive(now)) ) {
|
||||||
|
if (_paths[p].path->address().ss_family == AF_INET) {
|
||||||
|
const uint64_t s = _pathScore(p,now);
|
||||||
|
if (s >= best4) {
|
||||||
|
best4 = s;
|
||||||
|
bestp4 = (int)p;
|
||||||
|
}
|
||||||
|
} else if (_paths[p].path->address().ss_family == AF_INET6) {
|
||||||
|
const uint64_t s = _pathScore(p,now);
|
||||||
|
if (s >= best6) {
|
||||||
|
best6 = s;
|
||||||
|
bestp6 = (int)p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestp4 >= 0)
|
||||||
|
v4 = _paths[bestp4].path->address();
|
||||||
|
if (bestp6 >= 0)
|
||||||
|
v6 = _paths[bestp6].path->address();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
#include "../include/ZeroTierOne.h"
|
#include "../include/ZeroTierOne.h"
|
||||||
|
|
||||||
#include "RuntimeEnvironment.hpp"
|
#include "RuntimeEnvironment.hpp"
|
||||||
#include "CertificateOfMembership.hpp"
|
|
||||||
#include "Path.hpp"
|
#include "Path.hpp"
|
||||||
#include "Address.hpp"
|
#include "Address.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
@@ -44,10 +43,6 @@
|
|||||||
#include "Mutex.hpp"
|
#include "Mutex.hpp"
|
||||||
#include "NonCopyable.hpp"
|
#include "NonCopyable.hpp"
|
||||||
|
|
||||||
// Very rough computed estimate: (8 + 256 + 80 + (16 * 64) + (128 * 256) + (128 * 16))
|
|
||||||
// 1048576 provides tons of headroom -- overflow would just cause peer not to be persisted
|
|
||||||
#define ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE 1048576
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,18 +68,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity);
|
Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity);
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Time peer record was last used in any way
|
|
||||||
*/
|
|
||||||
inline uint64_t lastUsed() const throw() { return _lastUsed; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log a use of this peer record (done by Topology when peers are looked up)
|
|
||||||
*
|
|
||||||
* @param now New time of last use
|
|
||||||
*/
|
|
||||||
inline void use(uint64_t now) throw() { _lastUsed = now; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return This peer's ZT address (short for identity().address())
|
* @return This peer's ZT address (short for identity().address())
|
||||||
*/
|
*/
|
||||||
@@ -101,149 +84,162 @@ public:
|
|||||||
* This is called by the decode pipe when a packet is proven to be authentic
|
* This is called by the decode pipe when a packet is proven to be authentic
|
||||||
* and appears to be valid.
|
* and appears to be valid.
|
||||||
*
|
*
|
||||||
* @param RR Runtime environment
|
* @param path Path over which packet was received
|
||||||
* @param localAddr Local address
|
|
||||||
* @param remoteAddr Internet address of sender
|
|
||||||
* @param hops ZeroTier (not IP) hops
|
* @param hops ZeroTier (not IP) hops
|
||||||
* @param packetId Packet ID
|
* @param packetId Packet ID
|
||||||
* @param verb Packet verb
|
* @param verb Packet verb
|
||||||
* @param inRePacketId Packet ID in reply to (default: none)
|
* @param inRePacketId Packet ID in reply to (default: none)
|
||||||
* @param inReVerb Verb in reply to (for OK/ERROR, default: VERB_NOP)
|
* @param inReVerb Verb in reply to (for OK/ERROR, default: VERB_NOP)
|
||||||
|
* @param trustEstablished If true, some form of non-trivial trust (like allowed in network) has been established
|
||||||
*/
|
*/
|
||||||
void received(
|
void received(
|
||||||
const InetAddress &localAddr,
|
const SharedPtr<Path> &path,
|
||||||
const InetAddress &remoteAddr,
|
const unsigned int hops,
|
||||||
unsigned int hops,
|
const uint64_t packetId,
|
||||||
uint64_t packetId,
|
const Packet::Verb verb,
|
||||||
Packet::Verb verb,
|
const uint64_t inRePacketId,
|
||||||
uint64_t inRePacketId = 0,
|
const Packet::Verb inReVerb,
|
||||||
Packet::Verb inReVerb = Packet::VERB_NOP);
|
const bool trustEstablished);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current best direct path to this peer
|
|
||||||
*
|
|
||||||
* @param now Current time
|
|
||||||
* @return Best path or NULL if there are no active direct paths
|
|
||||||
*/
|
|
||||||
inline Path *getBestPath(uint64_t now) { return _getBestPath(now); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param now Current time
|
* @param now Current time
|
||||||
* @param addr Remote address
|
* @param addr Remote address
|
||||||
* @return True if we have an active path to this destination
|
* @return True if we have an active path to this destination
|
||||||
*/
|
*/
|
||||||
inline bool hasActivePathTo(uint64_t now,const InetAddress &addr) const
|
bool hasActivePathTo(uint64_t now,const InetAddress &addr) const;
|
||||||
{
|
|
||||||
for(unsigned int p=0;p<_numPaths;++p) {
|
|
||||||
if ((_paths[p].active(now))&&(_paths[p].address() == addr))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set all paths in the same ss_family that are not this one to cluster suboptimal
|
* Set which known path for an address family is optimal
|
||||||
*
|
|
||||||
* Addresses in other families are not affected.
|
|
||||||
*
|
*
|
||||||
* @param addr Address to make exclusive
|
* @param addr Address to make exclusive
|
||||||
*/
|
*/
|
||||||
inline void setClusterOptimalPathForAddressFamily(const InetAddress &addr)
|
inline void setClusterOptimal(const InetAddress &addr)
|
||||||
{
|
{
|
||||||
for(unsigned int p=0;p<_numPaths;++p) {
|
if (addr.ss_family == AF_INET) {
|
||||||
if (_paths[p].address().ss_family == addr.ss_family) {
|
_remoteClusterOptimal4 = (uint32_t)reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_addr.s_addr;
|
||||||
_paths[p].setClusterSuboptimal(_paths[p].address() != addr);
|
} else if (addr.ss_family == AF_INET6) {
|
||||||
}
|
memcpy(_remoteClusterOptimal6,reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_addr.s6_addr,16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send via best path
|
* Send via best direct path
|
||||||
*
|
*
|
||||||
* @param data Packet data
|
* @param data Packet data
|
||||||
* @param len Packet length
|
* @param len Packet length
|
||||||
* @param now Current time
|
* @param now Current time
|
||||||
* @return Path used on success or NULL on failure
|
* @param forceEvenIfDead If true, send even if the path is not 'alive'
|
||||||
|
* @return True if we actually sent something
|
||||||
*/
|
*/
|
||||||
inline Path *send(const void *data,unsigned int len,uint64_t now)
|
bool sendDirect(const void *data,unsigned int len,uint64_t now,bool forceEvenIfDead);
|
||||||
{
|
|
||||||
Path *const bestPath = getBestPath(now);
|
/**
|
||||||
if (bestPath) {
|
* Get the best current direct path
|
||||||
if (bestPath->send(RR,data,len,now))
|
*
|
||||||
return bestPath;
|
* @param now Current time
|
||||||
}
|
* @param includeExpired If true, include even expired paths
|
||||||
return (Path *)0;
|
* @return Best current path or NULL if none
|
||||||
}
|
*/
|
||||||
|
SharedPtr<Path> getBestPath(uint64_t now,bool includeExpired);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a HELLO to this peer at a specified physical address
|
* Send a HELLO to this peer at a specified physical address
|
||||||
*
|
*
|
||||||
* This does not update any statistics. It's used to send initial HELLOs
|
* No statistics or sent times are updated here.
|
||||||
* for NAT traversal and path verification.
|
|
||||||
*
|
*
|
||||||
* @param localAddr Local address
|
* @param localAddr Local address
|
||||||
* @param atAddress Destination address
|
* @param atAddress Destination address
|
||||||
* @param now Current time
|
* @param now Current time
|
||||||
* @param ttl Desired IP TTL (default: 0 to leave alone)
|
* @param counter Outgoing packet counter
|
||||||
*/
|
*/
|
||||||
void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl = 0);
|
void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send ECHO (or HELLO for older peers) to this peer at the given address
|
||||||
|
*
|
||||||
|
* No statistics or sent times are updated here.
|
||||||
|
*
|
||||||
|
* @param localAddr Local address
|
||||||
|
* @param atAddress Destination address
|
||||||
|
* @param now Current time
|
||||||
|
* @param sendFullHello If true, always send a full HELLO instead of just an ECHO
|
||||||
|
* @param counter Outgoing packet counter
|
||||||
|
*/
|
||||||
|
void attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try a memorized or statically defined path if any are known
|
||||||
|
*
|
||||||
|
* Under the hood this is done periodically based on ZT_TRY_MEMORIZED_PATH_INTERVAL.
|
||||||
|
*/
|
||||||
|
void tryMemorizedPath(uint64_t now);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send pings or keepalives depending on configured timeouts
|
* Send pings or keepalives depending on configured timeouts
|
||||||
*
|
*
|
||||||
* @param now Current time
|
* @param now Current time
|
||||||
* @param inetAddressFamily Keep this address family alive, or 0 to simply pick current best ignoring family
|
* @param inetAddressFamily Keep this address family alive, or -1 for any
|
||||||
* @return True if at least one direct path seems alive
|
* @return True if we have at least one direct path of the given family (or any if family is -1)
|
||||||
*/
|
*/
|
||||||
bool doPingAndKeepalive(uint64_t now,int inetAddressFamily);
|
bool doPingAndKeepalive(uint64_t now,int inetAddressFamily);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Push direct paths back to self if we haven't done so in the configured timeout
|
|
||||||
*
|
|
||||||
* @param localAddr Local address
|
|
||||||
* @param toAddress Remote address to send push to (usually from path)
|
|
||||||
* @param now Current time
|
* @param now Current time
|
||||||
* @param force If true, push regardless of rate limit
|
* @return True if this peer has at least one active and alive direct path
|
||||||
* @param includePrivatePaths If true, include local interface address paths (should only be done to peers with a trust relationship)
|
|
||||||
* @return True if something was actually sent
|
|
||||||
*/
|
*/
|
||||||
bool pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAddress,uint64_t now,bool force,bool includePrivatePaths);
|
bool hasActiveDirectPath(uint64_t now) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return All known direct paths to this peer (active or inactive)
|
* Reset paths within a given IP scope and address family
|
||||||
|
*
|
||||||
|
* Resetting a path involves sending an ECHO to it and then deactivating
|
||||||
|
* it until or unless it responds.
|
||||||
|
*
|
||||||
|
* @param scope IP scope
|
||||||
|
* @param inetAddressFamily Family e.g. AF_INET
|
||||||
|
* @param now Current time
|
||||||
*/
|
*/
|
||||||
inline std::vector<Path> paths() const
|
void resetWithinScope(InetAddress::IpScope scope,int inetAddressFamily,uint64_t now);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get most recently active path addresses for IPv4 and/or IPv6
|
||||||
|
*
|
||||||
|
* Note that v4 and v6 are not modified if they are not found, so
|
||||||
|
* initialize these to a NULL address to be able to check.
|
||||||
|
*
|
||||||
|
* @param now Current time
|
||||||
|
* @param v4 Result parameter to receive active IPv4 address, if any
|
||||||
|
* @param v6 Result parameter to receive active IPv6 address, if any
|
||||||
|
*/
|
||||||
|
void getRendezvousAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param now Current time
|
||||||
|
* @return All known direct paths to this peer and whether they are expired (true == expired)
|
||||||
|
*/
|
||||||
|
inline std::vector< std::pair< SharedPtr<Path>,bool > > paths(const uint64_t now) const
|
||||||
{
|
{
|
||||||
std::vector<Path> pp;
|
std::vector< std::pair< SharedPtr<Path>,bool > > pp;
|
||||||
|
Mutex::Lock _l(_paths_m);
|
||||||
for(unsigned int p=0,np=_numPaths;p<np;++p)
|
for(unsigned int p=0,np=_numPaths;p<np;++p)
|
||||||
pp.push_back(_paths[p]);
|
pp.push_back(std::pair< SharedPtr<Path>,bool >(_paths[p].path,(now - _paths[p].lastReceive) > ZT_PEER_PATH_EXPIRATION));
|
||||||
return pp;
|
return pp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Time of last receive of anything, whether direct or relayed
|
* @return Time of last receive of anything, whether direct or relayed
|
||||||
*/
|
*/
|
||||||
inline uint64_t lastReceive() const throw() { return _lastReceive; }
|
inline uint64_t lastReceive() const { return _lastReceive; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Time of most recent unicast frame received
|
* @return True if we've heard from this peer in less than ZT_PEER_ACTIVITY_TIMEOUT
|
||||||
*/
|
*/
|
||||||
inline uint64_t lastUnicastFrame() const throw() { return _lastUnicastFrame; }
|
inline bool isAlive(const uint64_t now) const { return ((now - _lastReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Time of most recent multicast frame received
|
|
||||||
*/
|
|
||||||
inline uint64_t lastMulticastFrame() const throw() { return _lastMulticastFrame; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Time of most recent frame of any kind (unicast or multicast)
|
|
||||||
*/
|
|
||||||
inline uint64_t lastFrame() const throw() { return std::max(_lastUnicastFrame,_lastMulticastFrame); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if this peer has sent us real network traffic recently
|
* @return True if this peer has sent us real network traffic recently
|
||||||
*/
|
*/
|
||||||
inline uint64_t activelyTransferringFrames(uint64_t now) const throw() { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); }
|
inline uint64_t isActive(uint64_t now) const { return ((now - _lastNontrivialReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Latency in milliseconds or 0 if unknown
|
* @return Latency in milliseconds or 0 if unknown
|
||||||
@@ -269,7 +265,7 @@ public:
|
|||||||
unsigned int l = _latency;
|
unsigned int l = _latency;
|
||||||
if (!l)
|
if (!l)
|
||||||
l = 0xffff;
|
l = 0xffff;
|
||||||
return (l * (((unsigned int)tsr / (ZT_PEER_DIRECT_PING_DELAY + 1000)) + 1));
|
return (l * (((unsigned int)tsr / (ZT_PEER_PING_PERIOD + 1000)) + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -285,47 +281,25 @@ public:
|
|||||||
else _latency = std::min(l,(unsigned int)65535);
|
else _latency = std::min(l,(unsigned int)65535);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param now Current time
|
|
||||||
* @return True if this peer has at least one active direct path
|
|
||||||
*/
|
|
||||||
inline bool hasActiveDirectPath(uint64_t now) const
|
|
||||||
{
|
|
||||||
for(unsigned int p=0;p<_numPaths;++p) {
|
|
||||||
if (_paths[p].active(now))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
/**
|
/**
|
||||||
* @param now Current time
|
* @param now Current time
|
||||||
* @return True if this peer has at least one active direct path that is not cluster-suboptimal
|
* @return True if this peer has at least one active direct path that is not cluster-suboptimal
|
||||||
*/
|
*/
|
||||||
inline bool hasClusterOptimalPath(uint64_t now) const
|
inline bool hasLocalClusterOptimalPath(uint64_t now) const
|
||||||
{
|
{
|
||||||
for(unsigned int p=0,np=_numPaths;p<np;++p) {
|
for(unsigned int p=0,np=_numPaths;p<np;++p) {
|
||||||
if ((_paths[p].active(now))&&(!_paths[p].isClusterSuboptimal()))
|
if ( (_paths[p].path->alive(now)) && (!_paths[p].localClusterSuboptimal) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset paths within a given scope
|
|
||||||
*
|
|
||||||
* @param scope IP scope of paths to reset
|
|
||||||
* @param now Current time
|
|
||||||
* @return True if at least one path was forgotten
|
|
||||||
*/
|
|
||||||
bool resetWithinScope(InetAddress::IpScope scope,uint64_t now);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return 256-bit secret symmetric encryption key
|
* @return 256-bit secret symmetric encryption key
|
||||||
*/
|
*/
|
||||||
inline const unsigned char *key() const throw() { return _key; }
|
inline const unsigned char *key() const { return _key; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the currently known remote version of this peer's client
|
* Set the currently known remote version of this peer's client
|
||||||
@@ -343,69 +317,22 @@ public:
|
|||||||
_vRevision = (uint16_t)vrev;
|
_vRevision = (uint16_t)vrev;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline unsigned int remoteVersionProtocol() const throw() { return _vProto; }
|
inline unsigned int remoteVersionProtocol() const { return _vProto; }
|
||||||
inline unsigned int remoteVersionMajor() const throw() { return _vMajor; }
|
inline unsigned int remoteVersionMajor() const { return _vMajor; }
|
||||||
inline unsigned int remoteVersionMinor() const throw() { return _vMinor; }
|
inline unsigned int remoteVersionMinor() const { return _vMinor; }
|
||||||
inline unsigned int remoteVersionRevision() const throw() { return _vRevision; }
|
inline unsigned int remoteVersionRevision() const { return _vRevision; }
|
||||||
|
|
||||||
inline bool remoteVersionKnown() const throw() { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
|
inline bool remoteVersionKnown() const { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get most recently active path addresses for IPv4 and/or IPv6
|
* @return True if peer has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
|
||||||
*
|
|
||||||
* Note that v4 and v6 are not modified if they are not found, so
|
|
||||||
* initialize these to a NULL address to be able to check.
|
|
||||||
*
|
|
||||||
* @param now Current time
|
|
||||||
* @param v4 Result parameter to receive active IPv4 address, if any
|
|
||||||
* @param v6 Result parameter to receive active IPv6 address, if any
|
|
||||||
*/
|
*/
|
||||||
void getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const;
|
inline bool trustEstablished(const uint64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check network COM agreement with this peer
|
* Rate limit gate for VERB_PUSH_DIRECT_PATHS
|
||||||
*
|
|
||||||
* @param nwid Network ID
|
|
||||||
* @param com Another certificate of membership
|
|
||||||
* @return True if supplied COM agrees with ours, false if not or if we don't have one
|
|
||||||
*/
|
*/
|
||||||
bool networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfMembership &com) const;
|
inline bool rateGatePushDirectPaths(const uint64_t now)
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the validity of the COM and add/update if valid and new
|
|
||||||
*
|
|
||||||
* @param nwid Network ID
|
|
||||||
* @param com Externally supplied COM
|
|
||||||
*/
|
|
||||||
bool validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param nwid Network ID
|
|
||||||
* @param now Current time
|
|
||||||
* @param updateLastPushedTime If true, go ahead and update the last pushed time regardless of return value
|
|
||||||
* @return Whether or not this peer needs another COM push from us
|
|
||||||
*/
|
|
||||||
bool needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform periodic cleaning operations
|
|
||||||
*
|
|
||||||
* @param now Current time
|
|
||||||
*/
|
|
||||||
void clean(uint64_t now);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update direct path push stats and return true if we should respond
|
|
||||||
*
|
|
||||||
* This is a circuit breaker to make VERB_PUSH_DIRECT_PATHS not particularly
|
|
||||||
* useful as a DDOS amplification attack vector. Otherwise a malicious peer
|
|
||||||
* could send loads of these and cause others to bombard arbitrary IPs with
|
|
||||||
* traffic.
|
|
||||||
*
|
|
||||||
* @param now Current time
|
|
||||||
* @return True if we should respond
|
|
||||||
*/
|
|
||||||
inline bool shouldRespondToDirectPathPush(const uint64_t now)
|
|
||||||
{
|
{
|
||||||
if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
|
if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
|
||||||
++_directPathPushCutoffCount;
|
++_directPathPushCutoffCount;
|
||||||
@@ -415,187 +342,145 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a common set of addresses by which two peers can link, if any
|
* Rate limit gate for VERB_NETWORK_CREDENTIALS
|
||||||
*
|
|
||||||
* @param a Peer A
|
|
||||||
* @param b Peer B
|
|
||||||
* @param now Current time
|
|
||||||
* @return Pair: B's address (to send to A), A's address (to send to B)
|
|
||||||
*/
|
*/
|
||||||
static inline std::pair<InetAddress,InetAddress> findCommonGround(const Peer &a,const Peer &b,uint64_t now)
|
inline bool rateGateCredentialsReceived(const uint64_t now)
|
||||||
{
|
{
|
||||||
std::pair<InetAddress,InetAddress> v4,v6;
|
if ((now - _lastCredentialsReceived) <= ZT_PEER_CREDENTIALS_CUTOFF_TIME)
|
||||||
b.getBestActiveAddresses(now,v4.first,v6.first);
|
++_credentialsCutoffCount;
|
||||||
a.getBestActiveAddresses(now,v4.second,v6.second);
|
else _credentialsCutoffCount = 0;
|
||||||
if ((v6.first)&&(v6.second)) // prefer IPv6 if both have it since NAT-t is (almost) unnecessary
|
_lastCredentialsReceived = now;
|
||||||
return v6;
|
return (_directPathPushCutoffCount < ZT_PEER_CREDEITIALS_CUTOFF_LIMIT);
|
||||||
else if ((v4.first)&&(v4.second))
|
|
||||||
return v4;
|
|
||||||
else return std::pair<InetAddress,InetAddress>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<unsigned int C>
|
|
||||||
inline void serialize(Buffer<C> &b) const
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_networkComs_m);
|
|
||||||
|
|
||||||
const unsigned int recSizePos = b.size();
|
|
||||||
b.addSize(4); // space for uint32_t field length
|
|
||||||
|
|
||||||
b.append((uint16_t)1); // version of serialized Peer data
|
|
||||||
|
|
||||||
_id.serialize(b,false);
|
|
||||||
|
|
||||||
b.append((uint64_t)_lastUsed);
|
|
||||||
b.append((uint64_t)_lastReceive);
|
|
||||||
b.append((uint64_t)_lastUnicastFrame);
|
|
||||||
b.append((uint64_t)_lastMulticastFrame);
|
|
||||||
b.append((uint64_t)_lastAnnouncedTo);
|
|
||||||
b.append((uint64_t)_lastDirectPathPushSent);
|
|
||||||
b.append((uint64_t)_lastDirectPathPushReceive);
|
|
||||||
b.append((uint64_t)_lastPathSort);
|
|
||||||
b.append((uint16_t)_vProto);
|
|
||||||
b.append((uint16_t)_vMajor);
|
|
||||||
b.append((uint16_t)_vMinor);
|
|
||||||
b.append((uint16_t)_vRevision);
|
|
||||||
b.append((uint32_t)_latency);
|
|
||||||
b.append((uint16_t)_directPathPushCutoffCount);
|
|
||||||
|
|
||||||
b.append((uint16_t)_numPaths);
|
|
||||||
for(unsigned int i=0;i<_numPaths;++i)
|
|
||||||
_paths[i].serialize(b);
|
|
||||||
|
|
||||||
b.append((uint32_t)_networkComs.size());
|
|
||||||
{
|
|
||||||
uint64_t *k = (uint64_t *)0;
|
|
||||||
_NetworkCom *v = (_NetworkCom *)0;
|
|
||||||
Hashtable<uint64_t,_NetworkCom>::Iterator i(const_cast<Peer *>(this)->_networkComs);
|
|
||||||
while (i.next(k,v)) {
|
|
||||||
b.append((uint64_t)*k);
|
|
||||||
b.append((uint64_t)v->ts);
|
|
||||||
v->com.serialize(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.append((uint32_t)_lastPushedComs.size());
|
|
||||||
{
|
|
||||||
uint64_t *k = (uint64_t *)0;
|
|
||||||
uint64_t *v = (uint64_t *)0;
|
|
||||||
Hashtable<uint64_t,uint64_t>::Iterator i(const_cast<Peer *>(this)->_lastPushedComs);
|
|
||||||
while (i.next(k,v)) {
|
|
||||||
b.append((uint64_t)*k);
|
|
||||||
b.append((uint64_t)*v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.template setAt<uint32_t>(recSizePos,(uint32_t)(b.size() - (recSizePos + 4))); // set size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new Peer from a serialized instance
|
* Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE
|
||||||
*
|
|
||||||
* @param renv Runtime environment
|
|
||||||
* @param myIdentity This node's identity
|
|
||||||
* @param b Buffer containing serialized Peer data
|
|
||||||
* @param p Pointer to current position in buffer, will be updated in place as buffer is read (value/result)
|
|
||||||
* @return New instance of Peer or NULL if serialized data was corrupt or otherwise invalid (may also throw an exception via Buffer)
|
|
||||||
*/
|
*/
|
||||||
template<unsigned int C>
|
inline bool rateGateRequestCredentials(const uint64_t now)
|
||||||
static inline SharedPtr<Peer> deserializeNew(const RuntimeEnvironment *renv,const Identity &myIdentity,const Buffer<C> &b,unsigned int &p)
|
|
||||||
{
|
{
|
||||||
const unsigned int recSize = b.template at<uint32_t>(p); p += 4;
|
if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||||
if ((p + recSize) > b.size())
|
_lastCredentialRequestSent = now;
|
||||||
return SharedPtr<Peer>(); // size invalid
|
return true;
|
||||||
if (b.template at<uint16_t>(p) != 1)
|
|
||||||
return SharedPtr<Peer>(); // version mismatch
|
|
||||||
p += 2;
|
|
||||||
|
|
||||||
Identity npid;
|
|
||||||
p += npid.deserialize(b,p);
|
|
||||||
if (!npid)
|
|
||||||
return SharedPtr<Peer>();
|
|
||||||
|
|
||||||
SharedPtr<Peer> np(new Peer(renv,myIdentity,npid));
|
|
||||||
|
|
||||||
np->_lastUsed = b.template at<uint64_t>(p); p += 8;
|
|
||||||
np->_lastReceive = b.template at<uint64_t>(p); p += 8;
|
|
||||||
np->_lastUnicastFrame = b.template at<uint64_t>(p); p += 8;
|
|
||||||
np->_lastMulticastFrame = b.template at<uint64_t>(p); p += 8;
|
|
||||||
np->_lastAnnouncedTo = b.template at<uint64_t>(p); p += 8;
|
|
||||||
np->_lastDirectPathPushSent = b.template at<uint64_t>(p); p += 8;
|
|
||||||
np->_lastDirectPathPushReceive = b.template at<uint64_t>(p); p += 8;
|
|
||||||
np->_lastPathSort = b.template at<uint64_t>(p); p += 8;
|
|
||||||
np->_vProto = b.template at<uint16_t>(p); p += 2;
|
|
||||||
np->_vMajor = b.template at<uint16_t>(p); p += 2;
|
|
||||||
np->_vMinor = b.template at<uint16_t>(p); p += 2;
|
|
||||||
np->_vRevision = b.template at<uint16_t>(p); p += 2;
|
|
||||||
np->_latency = b.template at<uint32_t>(p); p += 4;
|
|
||||||
np->_directPathPushCutoffCount = b.template at<uint16_t>(p); p += 2;
|
|
||||||
|
|
||||||
const unsigned int numPaths = b.template at<uint16_t>(p); p += 2;
|
|
||||||
for(unsigned int i=0;i<numPaths;++i) {
|
|
||||||
if (i < ZT_MAX_PEER_NETWORK_PATHS) {
|
|
||||||
p += np->_paths[np->_numPaths++].deserialize(b,p);
|
|
||||||
} else {
|
|
||||||
// Skip any paths beyond max, but still read stream
|
|
||||||
Path foo;
|
|
||||||
p += foo.deserialize(b,p);
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned int numNetworkComs = b.template at<uint32_t>(p); p += 4;
|
/**
|
||||||
for(unsigned int i=0;i<numNetworkComs;++i) {
|
* Rate limit gate for inbound WHOIS requests
|
||||||
_NetworkCom &c = np->_networkComs[b.template at<uint64_t>(p)]; p += 8;
|
*/
|
||||||
c.ts = b.template at<uint64_t>(p); p += 8;
|
inline bool rateGateInboundWhoisRequest(const uint64_t now)
|
||||||
p += c.com.deserialize(b,p);
|
{
|
||||||
|
if ((now - _lastWhoisRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||||
|
_lastWhoisRequestReceived = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned int numLastPushed = b.template at<uint32_t>(p); p += 4;
|
/**
|
||||||
for(unsigned int i=0;i<numLastPushed;++i) {
|
* Rate limit gate for inbound ECHO requests
|
||||||
const uint64_t nwid = b.template at<uint64_t>(p); p += 8;
|
*/
|
||||||
const uint64_t ts = b.template at<uint64_t>(p); p += 8;
|
inline bool rateGateEchoRequest(const uint64_t now)
|
||||||
np->_lastPushedComs.set(nwid,ts);
|
{
|
||||||
|
if ((now - _lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||||
|
_lastEchoRequestReceived = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return np;
|
/**
|
||||||
|
* Rate gate incoming requests for network COM
|
||||||
|
*/
|
||||||
|
inline bool rateGateIncomingComRequest(const uint64_t now)
|
||||||
|
{
|
||||||
|
if ((now - _lastComRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||||
|
_lastComRequestReceived = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rate gate outgoing requests for network COM
|
||||||
|
*/
|
||||||
|
inline bool rateGateOutgoingComRequest(const uint64_t now)
|
||||||
|
{
|
||||||
|
if ((now - _lastComRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||||
|
_lastComRequestSent = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _doDeadPathDetection(Path &p,const uint64_t now);
|
inline uint64_t _pathScore(const unsigned int p,const uint64_t now) const
|
||||||
Path *_getBestPath(const uint64_t now);
|
{
|
||||||
Path *_getBestPath(const uint64_t now,int inetAddressFamily);
|
uint64_t s = ZT_PEER_PING_PERIOD + _paths[p].lastReceive + (uint64_t)(_paths[p].path->preferenceRank() * (ZT_PEER_PING_PERIOD / ZT_PATH_MAX_PREFERENCE_RANK));
|
||||||
|
|
||||||
unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; // computed with key agreement, not serialized
|
if (_paths[p].path->address().ss_family == AF_INET) {
|
||||||
|
s += (uint64_t)(ZT_PEER_PING_PERIOD * (unsigned long)(reinterpret_cast<const struct sockaddr_in *>(&(_paths[p].path->address()))->sin_addr.s_addr == _remoteClusterOptimal4));
|
||||||
|
} else if (_paths[p].path->address().ss_family == AF_INET6) {
|
||||||
|
uint64_t clusterWeight = ZT_PEER_PING_PERIOD;
|
||||||
|
const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&(_paths[p].path->address()))->sin6_addr.s6_addr);
|
||||||
|
for(long i=0;i<16;++i) {
|
||||||
|
if (a[i] != _remoteClusterOptimal6[i]) {
|
||||||
|
clusterWeight = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s += clusterWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
s += (ZT_PEER_PING_PERIOD / 2) * (uint64_t)_paths[p].path->alive(now);
|
||||||
|
|
||||||
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
s -= ZT_PEER_PING_PERIOD * (uint64_t)_paths[p].localClusterSuboptimal;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH];
|
||||||
|
|
||||||
const RuntimeEnvironment *RR;
|
const RuntimeEnvironment *RR;
|
||||||
uint64_t _lastUsed;
|
|
||||||
uint64_t _lastReceive; // direct or indirect
|
uint64_t _lastReceive; // direct or indirect
|
||||||
uint64_t _lastUnicastFrame;
|
uint64_t _lastNontrivialReceive; // frames, things like netconf, etc.
|
||||||
uint64_t _lastMulticastFrame;
|
uint64_t _lastTriedMemorizedPath;
|
||||||
uint64_t _lastAnnouncedTo;
|
|
||||||
uint64_t _lastDirectPathPushSent;
|
uint64_t _lastDirectPathPushSent;
|
||||||
uint64_t _lastDirectPathPushReceive;
|
uint64_t _lastDirectPathPushReceive;
|
||||||
uint64_t _lastPathSort;
|
uint64_t _lastCredentialRequestSent;
|
||||||
|
uint64_t _lastWhoisRequestReceived;
|
||||||
|
uint64_t _lastEchoRequestReceived;
|
||||||
|
uint64_t _lastComRequestReceived;
|
||||||
|
uint64_t _lastComRequestSent;
|
||||||
|
uint64_t _lastCredentialsReceived;
|
||||||
|
uint64_t _lastTrustEstablishedPacketReceived;
|
||||||
|
|
||||||
|
uint8_t _remoteClusterOptimal6[16];
|
||||||
|
uint32_t _remoteClusterOptimal4;
|
||||||
|
|
||||||
uint16_t _vProto;
|
uint16_t _vProto;
|
||||||
uint16_t _vMajor;
|
uint16_t _vMajor;
|
||||||
uint16_t _vMinor;
|
uint16_t _vMinor;
|
||||||
uint16_t _vRevision;
|
uint16_t _vRevision;
|
||||||
|
|
||||||
Identity _id;
|
Identity _id;
|
||||||
Path _paths[ZT_MAX_PEER_NETWORK_PATHS];
|
|
||||||
|
struct {
|
||||||
|
uint64_t lastReceive;
|
||||||
|
SharedPtr<Path> path;
|
||||||
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
bool localClusterSuboptimal;
|
||||||
|
#endif
|
||||||
|
} _paths[ZT_MAX_PEER_NETWORK_PATHS];
|
||||||
|
Mutex _paths_m;
|
||||||
|
|
||||||
unsigned int _numPaths;
|
unsigned int _numPaths;
|
||||||
unsigned int _latency;
|
unsigned int _latency;
|
||||||
unsigned int _directPathPushCutoffCount;
|
unsigned int _directPathPushCutoffCount;
|
||||||
|
unsigned int _credentialsCutoffCount;
|
||||||
struct _NetworkCom
|
|
||||||
{
|
|
||||||
_NetworkCom() {}
|
|
||||||
_NetworkCom(uint64_t t,const CertificateOfMembership &c) : ts(t),com(c) {}
|
|
||||||
uint64_t ts;
|
|
||||||
CertificateOfMembership com;
|
|
||||||
};
|
|
||||||
Hashtable<uint64_t,_NetworkCom> _networkComs;
|
|
||||||
Hashtable<uint64_t,uint64_t> _lastPushedComs;
|
|
||||||
Mutex _networkComs_m;
|
|
||||||
|
|
||||||
AtomicCounter __refCount;
|
AtomicCounter __refCount;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ class Multicaster;
|
|||||||
class NetworkController;
|
class NetworkController;
|
||||||
class SelfAwareness;
|
class SelfAwareness;
|
||||||
class Cluster;
|
class Cluster;
|
||||||
class DeferredPackets;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds global state for an instance of ZeroTier::Node
|
* Holds global state for an instance of ZeroTier::Node
|
||||||
@@ -51,11 +50,9 @@ public:
|
|||||||
,mc((Multicaster *)0)
|
,mc((Multicaster *)0)
|
||||||
,topology((Topology *)0)
|
,topology((Topology *)0)
|
||||||
,sa((SelfAwareness *)0)
|
,sa((SelfAwareness *)0)
|
||||||
,dp((DeferredPackets *)0)
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
,cluster((Cluster *)0)
|
,cluster((Cluster *)0)
|
||||||
#endif
|
#endif
|
||||||
,dpEnabled(0)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,15 +79,9 @@ public:
|
|||||||
Multicaster *mc;
|
Multicaster *mc;
|
||||||
Topology *topology;
|
Topology *topology;
|
||||||
SelfAwareness *sa;
|
SelfAwareness *sa;
|
||||||
DeferredPackets *dp;
|
|
||||||
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
Cluster *cluster;
|
Cluster *cluster;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// This is set to >0 if background threads are waiting on deferred
|
|
||||||
// packets, otherwise 'dp' should not be used.
|
|
||||||
volatile int dpEnabled;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ void Salsa20::init(const void *key,unsigned int kbits,const void *iv)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Salsa20::encrypt12(const void *in,void *out,unsigned int bytes)
|
void Salsa20::crypt12(const void *in,void *out,unsigned int bytes)
|
||||||
throw()
|
throw()
|
||||||
{
|
{
|
||||||
uint8_t tmp[64];
|
uint8_t tmp[64];
|
||||||
@@ -623,7 +623,7 @@ void Salsa20::encrypt12(const void *in,void *out,unsigned int bytes)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Salsa20::encrypt20(const void *in,void *out,unsigned int bytes)
|
void Salsa20::crypt20(const void *in,void *out,unsigned int bytes)
|
||||||
throw()
|
throw()
|
||||||
{
|
{
|
||||||
uint8_t tmp[64];
|
uint8_t tmp[64];
|
||||||
|
|||||||
@@ -56,51 +56,25 @@ public:
|
|||||||
throw();
|
throw();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypt data using Salsa20/12
|
* Encrypt/decrypt data using Salsa20/12
|
||||||
*
|
*
|
||||||
* @param in Input data
|
* @param in Input data
|
||||||
* @param out Output buffer
|
* @param out Output buffer
|
||||||
* @param bytes Length of data
|
* @param bytes Length of data
|
||||||
*/
|
*/
|
||||||
void encrypt12(const void *in,void *out,unsigned int bytes)
|
void crypt12(const void *in,void *out,unsigned int bytes)
|
||||||
throw();
|
throw();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypt data using Salsa20/20
|
* Encrypt/decrypt data using Salsa20/20
|
||||||
*
|
*
|
||||||
* @param in Input data
|
* @param in Input data
|
||||||
* @param out Output buffer
|
* @param out Output buffer
|
||||||
* @param bytes Length of data
|
* @param bytes Length of data
|
||||||
*/
|
*/
|
||||||
void encrypt20(const void *in,void *out,unsigned int bytes)
|
void crypt20(const void *in,void *out,unsigned int bytes)
|
||||||
throw();
|
throw();
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt data
|
|
||||||
*
|
|
||||||
* @param in Input data
|
|
||||||
* @param out Output buffer
|
|
||||||
* @param bytes Length of data
|
|
||||||
*/
|
|
||||||
inline void decrypt12(const void *in,void *out,unsigned int bytes)
|
|
||||||
throw()
|
|
||||||
{
|
|
||||||
encrypt12(in,out,bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt data
|
|
||||||
*
|
|
||||||
* @param in Input data
|
|
||||||
* @param out Output buffer
|
|
||||||
* @param bytes Length of data
|
|
||||||
*/
|
|
||||||
inline void decrypt20(const void *in,void *out,unsigned int bytes)
|
|
||||||
throw()
|
|
||||||
{
|
|
||||||
encrypt20(in,out,bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
union {
|
union {
|
||||||
#ifdef ZT_SALSA20_SSE
|
#ifdef ZT_SALSA20_SSE
|
||||||
|
|||||||
@@ -33,37 +33,29 @@
|
|||||||
#include "Switch.hpp"
|
#include "Switch.hpp"
|
||||||
|
|
||||||
// Entry timeout -- make it fairly long since this is just to prevent stale buildup
|
// Entry timeout -- make it fairly long since this is just to prevent stale buildup
|
||||||
#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 3600000
|
#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 600000
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
class _ResetWithinScope
|
class _ResetWithinScope
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
_ResetWithinScope(uint64_t now,InetAddress::IpScope scope) :
|
_ResetWithinScope(uint64_t now,int inetAddressFamily,InetAddress::IpScope scope) :
|
||||||
_now(now),
|
_now(now),
|
||||||
|
_family(inetAddressFamily),
|
||||||
_scope(scope) {}
|
_scope(scope) {}
|
||||||
|
|
||||||
inline void operator()(Topology &t,const SharedPtr<Peer> &p)
|
inline void operator()(Topology &t,const SharedPtr<Peer> &p) { p->resetWithinScope(_scope,_family,_now); }
|
||||||
{
|
|
||||||
if (p->resetWithinScope(_scope,_now))
|
|
||||||
peersReset.push_back(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector< SharedPtr<Peer> > peersReset;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t _now;
|
uint64_t _now;
|
||||||
|
int _family;
|
||||||
InetAddress::IpScope _scope;
|
InetAddress::IpScope _scope;
|
||||||
};
|
};
|
||||||
|
|
||||||
SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
|
SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
|
||||||
RR(renv),
|
RR(renv),
|
||||||
_phy(32)
|
_phy(128)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SelfAwareness::~SelfAwareness()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,9 +71,11 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &receivedOnLoc
|
|||||||
|
|
||||||
if ( (trusted) && ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
|
if ( (trusted) && ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
|
||||||
// Changes to external surface reported by trusted peers causes path reset in this scope
|
// Changes to external surface reported by trusted peers causes path reset in this scope
|
||||||
|
TRACE("physical address %s for scope %u as seen from %s(%s) differs from %s, resetting paths in scope",myPhysicalAddress.toString().c_str(),(unsigned int)scope,reporter.toString().c_str(),reporterPhysicalAddress.toString().c_str(),entry.mySurface.toString().c_str());
|
||||||
|
|
||||||
entry.mySurface = myPhysicalAddress;
|
entry.mySurface = myPhysicalAddress;
|
||||||
entry.ts = now;
|
entry.ts = now;
|
||||||
TRACE("physical address %s for scope %u as seen from %s(%s) differs from %s, resetting paths in scope",myPhysicalAddress.toString().c_str(),(unsigned int)scope,reporter.toString().c_str(),reporterPhysicalAddress.toString().c_str(),entry.mySurface.toString().c_str());
|
entry.trusted = trusted;
|
||||||
|
|
||||||
// Erase all entries in this scope that were not reported from this remote address to prevent 'thrashing'
|
// Erase all entries in this scope that were not reported from this remote address to prevent 'thrashing'
|
||||||
// due to multiple reports of endpoint change.
|
// due to multiple reports of endpoint change.
|
||||||
@@ -96,23 +90,14 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &receivedOnLoc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset all paths within this scope
|
// Reset all paths within this scope and address family
|
||||||
_ResetWithinScope rset(now,(InetAddress::IpScope)scope);
|
_ResetWithinScope rset(now,myPhysicalAddress.ss_family,(InetAddress::IpScope)scope);
|
||||||
RR->topology->eachPeer<_ResetWithinScope &>(rset);
|
RR->topology->eachPeer<_ResetWithinScope &>(rset);
|
||||||
|
|
||||||
// Send a NOP to all peers for whom we forgot a path. This will cause direct
|
|
||||||
// links to be re-established if possible, possibly using a root server or some
|
|
||||||
// other relay.
|
|
||||||
for(std::vector< SharedPtr<Peer> >::const_iterator p(rset.peersReset.begin());p!=rset.peersReset.end();++p) {
|
|
||||||
if ((*p)->activelyTransferringFrames(now)) {
|
|
||||||
Packet outp((*p)->address(),RR->identity.address(),Packet::VERB_NOP);
|
|
||||||
RR->sw->send(outp,true,0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Otherwise just update DB to use to determine external surface info
|
// Otherwise just update DB to use to determine external surface info
|
||||||
entry.mySurface = myPhysicalAddress;
|
entry.mySurface = myPhysicalAddress;
|
||||||
entry.ts = now;
|
entry.ts = now;
|
||||||
|
entry.trusted = trusted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,49 +118,70 @@ std::vector<InetAddress> SelfAwareness::getSymmetricNatPredictions()
|
|||||||
/* This is based on ideas and strategies found here:
|
/* This is based on ideas and strategies found here:
|
||||||
* https://tools.ietf.org/html/draft-takeda-symmetric-nat-traversal-00
|
* https://tools.ietf.org/html/draft-takeda-symmetric-nat-traversal-00
|
||||||
*
|
*
|
||||||
* In short: a great many symmetric NATs allocate ports sequentially.
|
* For each IP address reported by a trusted (upstream) peer, we find
|
||||||
* This is common on enterprise and carrier grade NATs as well as consumer
|
* the external port most recently reported by ANY peer for that IP.
|
||||||
* devices. This code generates a list of "you might try this" addresses by
|
*
|
||||||
* extrapolating likely port assignments from currently known external
|
* We only do any of this for global IPv4 addresses since private IPs
|
||||||
* global IPv4 surfaces. These can then be included in a PUSH_DIRECT_PATHS
|
* and IPv6 are not going to have symmetric NAT.
|
||||||
* message to another peer, causing it to possibly try these addresses and
|
*
|
||||||
* bust our local symmetric NAT. It works often enough to be worth the
|
* SECURITY NOTE:
|
||||||
* extra bit of code and does no harm in cases where it fails. */
|
*
|
||||||
|
* We never use IPs reported by non-trusted peers, since this could lead
|
||||||
|
* to a minor vulnerability whereby a peer could poison our cache with
|
||||||
|
* bad external surface reports via OK(HELLO) and then possibly coax us
|
||||||
|
* into suggesting their IP to other peers via PUSH_DIRECT_PATHS. This
|
||||||
|
* in turn could allow them to MITM flows.
|
||||||
|
*
|
||||||
|
* Since flows are encrypted and authenticated they could not actually
|
||||||
|
* read or modify traffic, but they could gather meta-data for forensics
|
||||||
|
* purpsoes or use this as a DOS attack vector. */
|
||||||
|
|
||||||
// Gather unique surfaces indexed by local received-on address and flag
|
std::map< uint32_t,std::pair<uint64_t,unsigned int> > maxPortByIp;
|
||||||
// us as behind a symmetric NAT if there is more than one.
|
InetAddress theOneTrueSurface;
|
||||||
std::map< InetAddress,std::set<InetAddress> > surfaces;
|
|
||||||
bool symmetric = false;
|
bool symmetric = false;
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_phy_m);
|
Mutex::Lock _l(_phy_m);
|
||||||
|
|
||||||
|
{ // First get IPs from only trusted peers, and perform basic NAT type characterization
|
||||||
|
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
|
||||||
|
PhySurfaceKey *k = (PhySurfaceKey *)0;
|
||||||
|
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
|
||||||
|
while (i.next(k,e)) {
|
||||||
|
if ((e->trusted)&&(e->mySurface.ss_family == AF_INET)&&(e->mySurface.ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
|
||||||
|
if (!theOneTrueSurface)
|
||||||
|
theOneTrueSurface = e->mySurface;
|
||||||
|
else if (theOneTrueSurface != e->mySurface)
|
||||||
|
symmetric = true;
|
||||||
|
maxPortByIp[reinterpret_cast<const struct sockaddr_in *>(&(e->mySurface))->sin_addr.s_addr] = std::pair<uint64_t,unsigned int>(e->ts,e->mySurface.port());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Then find max port per IP from a trusted peer
|
||||||
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
|
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
|
||||||
PhySurfaceKey *k = (PhySurfaceKey *)0;
|
PhySurfaceKey *k = (PhySurfaceKey *)0;
|
||||||
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
|
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
|
||||||
while (i.next(k,e)) {
|
while (i.next(k,e)) {
|
||||||
if ((e->mySurface.ss_family == AF_INET)&&(e->mySurface.ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
|
if ((e->mySurface.ss_family == AF_INET)&&(e->mySurface.ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
|
||||||
std::set<InetAddress> &s = surfaces[k->receivedOnLocalAddress];
|
std::map< uint32_t,std::pair<uint64_t,unsigned int> >::iterator mp(maxPortByIp.find(reinterpret_cast<const struct sockaddr_in *>(&(e->mySurface))->sin_addr.s_addr));
|
||||||
s.insert(e->mySurface);
|
if ((mp != maxPortByIp.end())&&(mp->second.first < e->ts)) {
|
||||||
symmetric = symmetric||(s.size() > 1);
|
mp->second.first = e->ts;
|
||||||
|
mp->second.second = e->mySurface.port();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we appear to be symmetrically NATed, generate and return extrapolations
|
|
||||||
// of those surfaces. Since PUSH_DIRECT_PATHS is sent multiple times, we
|
|
||||||
// probabilistically generate extrapolations of anywhere from +1 to +5 to
|
|
||||||
// increase the odds that it will work "eventually".
|
|
||||||
if (symmetric) {
|
if (symmetric) {
|
||||||
std::vector<InetAddress> r;
|
std::vector<InetAddress> r;
|
||||||
for(std::map< InetAddress,std::set<InetAddress> >::iterator si(surfaces.begin());si!=surfaces.end();++si) {
|
for(unsigned int k=1;k<=3;++k) {
|
||||||
for(std::set<InetAddress>::iterator i(si->second.begin());i!=si->second.end();++i) {
|
for(std::map< uint32_t,std::pair<uint64_t,unsigned int> >::iterator i(maxPortByIp.begin());i!=maxPortByIp.end();++i) {
|
||||||
InetAddress ipp(*i);
|
unsigned int p = i->second.second + k;
|
||||||
unsigned int p = ipp.port() + 1 + ((unsigned int)RR->node->prng() & 3);
|
if (p > 65535) p -= 64511;
|
||||||
if (p >= 65535)
|
InetAddress pred(&(i->first),4,p);
|
||||||
p -= 64510; // NATs seldom use ports <=1024 so wrap to 1025
|
if (std::find(r.begin(),r.end(),pred) == r.end())
|
||||||
ipp.setPort(p);
|
r.push_back(pred);
|
||||||
if ((si->second.count(ipp) == 0)&&(std::find(r.begin(),r.end(),ipp) == r.end())) {
|
|
||||||
r.push_back(ipp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ class SelfAwareness
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SelfAwareness(const RuntimeEnvironment *renv);
|
SelfAwareness(const RuntimeEnvironment *renv);
|
||||||
~SelfAwareness();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a trusted remote peer informs us of our external network address
|
* Called when a trusted remote peer informs us of our external network address
|
||||||
@@ -82,9 +81,10 @@ private:
|
|||||||
{
|
{
|
||||||
InetAddress mySurface;
|
InetAddress mySurface;
|
||||||
uint64_t ts;
|
uint64_t ts;
|
||||||
|
bool trusted;
|
||||||
|
|
||||||
PhySurfaceEntry() : mySurface(),ts(0) {}
|
PhySurfaceEntry() : mySurface(),ts(0),trusted(false) {}
|
||||||
PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t) {}
|
PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t),trusted(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
const RuntimeEnvironment *RR;
|
const RuntimeEnvironment *RR;
|
||||||
|
|||||||
@@ -119,16 +119,40 @@ public:
|
|||||||
inline T *ptr() const throw() { return _ptr; }
|
inline T *ptr() const throw() { return _ptr; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this pointer to null
|
* Set this pointer to NULL
|
||||||
*/
|
*/
|
||||||
inline void zero()
|
inline void zero()
|
||||||
{
|
{
|
||||||
if (_ptr) {
|
if (_ptr) {
|
||||||
if (--_ptr->__refCount <= 0)
|
if (--_ptr->__refCount <= 0)
|
||||||
delete _ptr;
|
delete _ptr;
|
||||||
}
|
|
||||||
_ptr = (T *)0;
|
_ptr = (T *)0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this pointer to NULL if this is the only pointer holding the object
|
||||||
|
*
|
||||||
|
* @return True if object was deleted and SharedPtr is now NULL (or was already NULL)
|
||||||
|
*/
|
||||||
|
inline bool reclaimIfWeak()
|
||||||
|
{
|
||||||
|
if (_ptr) {
|
||||||
|
if (++_ptr->__refCount <= 2) {
|
||||||
|
if (--_ptr->__refCount <= 1) {
|
||||||
|
delete _ptr;
|
||||||
|
_ptr = (T *)0;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline bool operator==(const SharedPtr &sp) const throw() { return (_ptr == sp._ptr); }
|
inline bool operator==(const SharedPtr &sp) const throw() { return (_ptr == sp._ptr); }
|
||||||
inline bool operator!=(const SharedPtr &sp) const throw() { return (_ptr != sp._ptr); }
|
inline bool operator!=(const SharedPtr &sp) const throw() { return (_ptr != sp._ptr); }
|
||||||
|
|||||||
@@ -64,37 +64,36 @@ Switch::Switch(const RuntimeEnvironment *renv) :
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Switch::~Switch()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len)
|
void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
const uint64_t now = RR->node->now();
|
const uint64_t now = RR->node->now();
|
||||||
|
|
||||||
|
SharedPtr<Path> path(RR->topology->getPath(localAddr,fromAddr));
|
||||||
|
path->received(now);
|
||||||
|
|
||||||
if (len == 13) {
|
if (len == 13) {
|
||||||
/* LEGACY: before VERB_PUSH_DIRECT_PATHS, peers used broadcast
|
/* LEGACY: before VERB_PUSH_DIRECT_PATHS, peers used broadcast
|
||||||
* announcements on the LAN to solve the 'same network problem.' We
|
* announcements on the LAN to solve the 'same network problem.' We
|
||||||
* no longer send these, but we'll listen for them for a while to
|
* no longer send these, but we'll listen for them for a while to
|
||||||
* locate peers with versions <1.0.4. */
|
* locate peers with versions <1.0.4. */
|
||||||
|
|
||||||
Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
|
const Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
|
||||||
if (beaconAddr == RR->identity.address())
|
if (beaconAddr == RR->identity.address())
|
||||||
return;
|
return;
|
||||||
if (!RR->node->shouldUsePathForZeroTierTraffic(localAddr,fromAddr))
|
if (!RR->node->shouldUsePathForZeroTierTraffic(beaconAddr,localAddr,fromAddr))
|
||||||
return;
|
return;
|
||||||
SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr));
|
const SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr));
|
||||||
if (peer) { // we'll only respond to beacons from known peers
|
if (peer) { // we'll only respond to beacons from known peers
|
||||||
if ((now - _lastBeaconResponse) >= 2500) { // limit rate of responses
|
if ((now - _lastBeaconResponse) >= 2500) { // limit rate of responses
|
||||||
_lastBeaconResponse = now;
|
_lastBeaconResponse = now;
|
||||||
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
|
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
|
||||||
outp.armor(peer->key(),true);
|
outp.armor(peer->key(),true,path->nextOutgoingCounter());
|
||||||
RR->node->putPacket(localAddr,fromAddr,outp.data(),outp.size());
|
path->send(RR,outp.data(),outp.size(),now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { // min length check is important!
|
} else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { // SECURITY: min length check is important since we do some C-style stuff below!
|
||||||
if (reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) {
|
if (reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) {
|
||||||
// Handle fragment ----------------------------------------------------
|
// Handle fragment ----------------------------------------------------
|
||||||
|
|
||||||
@@ -102,25 +101,33 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
|||||||
const Address destination(fragment.destination());
|
const Address destination(fragment.destination());
|
||||||
|
|
||||||
if (destination != RR->identity.address()) {
|
if (destination != RR->identity.address()) {
|
||||||
// Fragment is not for us, so try to relay it
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
const bool isClusterFrontplane = ((RR->cluster)&&(RR->cluster->isClusterPeerFrontplane(fromAddr)));
|
||||||
|
#else
|
||||||
|
const bool isClusterFrontplane = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ( (!RR->topology->amRoot()) && (!path->trustEstablished(now)) && (!isClusterFrontplane) )
|
||||||
|
return;
|
||||||
|
|
||||||
if (fragment.hops() < ZT_RELAY_MAX_HOPS) {
|
if (fragment.hops() < ZT_RELAY_MAX_HOPS) {
|
||||||
fragment.incrementHops();
|
fragment.incrementHops();
|
||||||
|
|
||||||
// Note: we don't bother initiating NAT-t for fragments, since heads will set that off.
|
// Note: we don't bother initiating NAT-t for fragments, since heads will set that off.
|
||||||
// It wouldn't hurt anything, just redundant and unnecessary.
|
// It wouldn't hurt anything, just redundant and unnecessary.
|
||||||
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
|
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
|
||||||
if ((!relayTo)||(!relayTo->send(fragment.data(),fragment.size(),now))) {
|
if ((!relayTo)||(!relayTo->sendDirect(fragment.data(),fragment.size(),now,false))) {
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
if (RR->cluster) {
|
if ((RR->cluster)&&(!isClusterFrontplane)) {
|
||||||
RR->cluster->sendViaCluster(Address(),destination,fragment.data(),fragment.size(),false);
|
RR->cluster->relayViaCluster(Address(),destination,fragment.data(),fragment.size(),false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Don't know peer or no direct path -- so relay via root server
|
// Don't know peer or no direct path -- so relay via someone upstream
|
||||||
relayTo = RR->topology->getBestRoot();
|
relayTo = RR->topology->getUpstreamPeer();
|
||||||
if (relayTo)
|
if (relayTo)
|
||||||
relayTo->send(fragment.data(),fragment.size(),now);
|
relayTo->sendDirect(fragment.data(),fragment.size(),now,true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str());
|
TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str());
|
||||||
@@ -164,7 +171,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
|||||||
for(unsigned int f=1;f<totalFragments;++f)
|
for(unsigned int f=1;f<totalFragments;++f)
|
||||||
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
|
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
|
||||||
|
|
||||||
if (rq->frag0.tryDecode(RR,false)) {
|
if (rq->frag0.tryDecode(RR)) {
|
||||||
rq->timestamp = 0; // packet decoded, free entry
|
rq->timestamp = 0; // packet decoded, free entry
|
||||||
} else {
|
} else {
|
||||||
rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
|
rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
|
||||||
@@ -178,7 +185,107 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
|||||||
} else if (len >= ZT_PROTO_MIN_PACKET_LENGTH) { // min length check is important!
|
} else if (len >= ZT_PROTO_MIN_PACKET_LENGTH) { // min length check is important!
|
||||||
// Handle packet head -------------------------------------------------
|
// Handle packet head -------------------------------------------------
|
||||||
|
|
||||||
// See packet format in Packet.hpp to understand this
|
const Address destination(reinterpret_cast<const uint8_t *>(data) + 8,ZT_ADDRESS_LENGTH);
|
||||||
|
const Address source(reinterpret_cast<const uint8_t *>(data) + 13,ZT_ADDRESS_LENGTH);
|
||||||
|
|
||||||
|
//TRACE("<< %.16llx %s -> %s (size: %u)",(unsigned long long)packet->packetId(),source.toString().c_str(),destination.toString().c_str(),packet->size());
|
||||||
|
|
||||||
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
if ( (source == RR->identity.address()) && ((!RR->cluster)||(!RR->cluster->isClusterPeerFrontplane(fromAddr))) )
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
if (source == RR->identity.address())
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (destination != RR->identity.address()) {
|
||||||
|
if ( (!RR->topology->amRoot()) && (!path->trustEstablished(now)) && (source != RR->identity.address()) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Packet packet(data,len);
|
||||||
|
|
||||||
|
if (packet.hops() < ZT_RELAY_MAX_HOPS) {
|
||||||
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
if (source != RR->identity.address()) // don't increment hops for cluster frontplane relays
|
||||||
|
packet.incrementHops();
|
||||||
|
#else
|
||||||
|
packet.incrementHops();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
|
||||||
|
if ((relayTo)&&(relayTo->sendDirect(packet.data(),packet.size(),now,false))) {
|
||||||
|
if ((source != RR->identity.address())&&(_shouldUnite(now,source,destination))) { // don't send RENDEZVOUS for cluster frontplane relays
|
||||||
|
const InetAddress *hintToSource = (InetAddress *)0;
|
||||||
|
const InetAddress *hintToDest = (InetAddress *)0;
|
||||||
|
|
||||||
|
InetAddress destV4,destV6;
|
||||||
|
InetAddress sourceV4,sourceV6;
|
||||||
|
relayTo->getRendezvousAddresses(now,destV4,destV6);
|
||||||
|
|
||||||
|
const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(source));
|
||||||
|
if (sourcePeer) {
|
||||||
|
sourcePeer->getRendezvousAddresses(now,sourceV4,sourceV6);
|
||||||
|
if ((destV6)&&(sourceV6)) {
|
||||||
|
hintToSource = &destV6;
|
||||||
|
hintToDest = &sourceV6;
|
||||||
|
} else if ((destV4)&&(sourceV4)) {
|
||||||
|
hintToSource = &destV4;
|
||||||
|
hintToDest = &sourceV4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((hintToSource)&&(hintToDest)) {
|
||||||
|
unsigned int alt = (unsigned int)RR->node->prng() & 1; // randomize which hint we send first for obscure NAT-t reasons
|
||||||
|
const unsigned int completed = alt + 2;
|
||||||
|
while (alt != completed) {
|
||||||
|
if ((alt & 1) == 0) {
|
||||||
|
Packet outp(source,RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
||||||
|
outp.append((uint8_t)0);
|
||||||
|
destination.appendTo(outp);
|
||||||
|
outp.append((uint16_t)hintToSource->port());
|
||||||
|
if (hintToSource->ss_family == AF_INET6) {
|
||||||
|
outp.append((uint8_t)16);
|
||||||
|
outp.append(hintToSource->rawIpData(),16);
|
||||||
|
} else {
|
||||||
|
outp.append((uint8_t)4);
|
||||||
|
outp.append(hintToSource->rawIpData(),4);
|
||||||
|
}
|
||||||
|
send(outp,true);
|
||||||
|
} else {
|
||||||
|
Packet outp(destination,RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
||||||
|
outp.append((uint8_t)0);
|
||||||
|
source.appendTo(outp);
|
||||||
|
outp.append((uint16_t)hintToDest->port());
|
||||||
|
if (hintToDest->ss_family == AF_INET6) {
|
||||||
|
outp.append((uint8_t)16);
|
||||||
|
outp.append(hintToDest->rawIpData(),16);
|
||||||
|
} else {
|
||||||
|
outp.append((uint8_t)4);
|
||||||
|
outp.append(hintToDest->rawIpData(),4);
|
||||||
|
}
|
||||||
|
send(outp,true);
|
||||||
|
}
|
||||||
|
++alt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
if ((RR->cluster)&&(source != RR->identity.address())) {
|
||||||
|
RR->cluster->relayViaCluster(source,destination,packet.data(),packet.size(),_shouldUnite(now,source,destination));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
relayTo = RR->topology->getUpstreamPeer(&source,1,true);
|
||||||
|
if (relayTo)
|
||||||
|
relayTo->sendDirect(packet.data(),packet.size(),now,true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet.source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
|
||||||
|
}
|
||||||
|
} else if ((reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED) != 0) {
|
||||||
|
// Packet is the head of a fragmented packet series
|
||||||
|
|
||||||
const uint64_t packetId = (
|
const uint64_t packetId = (
|
||||||
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[0]) << 56) |
|
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[0]) << 56) |
|
||||||
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[1]) << 48) |
|
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[1]) << 48) |
|
||||||
@@ -189,55 +296,6 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
|||||||
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[6]) << 8) |
|
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[6]) << 8) |
|
||||||
((uint64_t)reinterpret_cast<const uint8_t *>(data)[7])
|
((uint64_t)reinterpret_cast<const uint8_t *>(data)[7])
|
||||||
);
|
);
|
||||||
const Address destination(reinterpret_cast<const uint8_t *>(data) + 8,ZT_ADDRESS_LENGTH);
|
|
||||||
const Address source(reinterpret_cast<const uint8_t *>(data) + 13,ZT_ADDRESS_LENGTH);
|
|
||||||
|
|
||||||
// Catch this and toss it -- it would never work, but it could happen if we somehow
|
|
||||||
// mistakenly guessed an address we're bound to as a destination for another peer.
|
|
||||||
if (source == RR->identity.address())
|
|
||||||
return;
|
|
||||||
|
|
||||||
//TRACE("<< %.16llx %s -> %s (size: %u)",(unsigned long long)packet->packetId(),source.toString().c_str(),destination.toString().c_str(),packet->size());
|
|
||||||
|
|
||||||
if (destination != RR->identity.address()) {
|
|
||||||
Packet packet(data,len);
|
|
||||||
|
|
||||||
// Packet is not for us, so try to relay it
|
|
||||||
if (packet.hops() < ZT_RELAY_MAX_HOPS) {
|
|
||||||
packet.incrementHops();
|
|
||||||
|
|
||||||
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
|
|
||||||
if ((relayTo)&&((relayTo->send(packet.data(),packet.size(),now)))) {
|
|
||||||
Mutex::Lock _l(_lastUniteAttempt_m);
|
|
||||||
uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
|
|
||||||
if ((now - luts) >= ZT_MIN_UNITE_INTERVAL) {
|
|
||||||
luts = now;
|
|
||||||
unite(source,destination);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
|
||||||
if (RR->cluster) {
|
|
||||||
bool shouldUnite;
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lastUniteAttempt_m);
|
|
||||||
uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
|
|
||||||
shouldUnite = ((now - luts) >= ZT_MIN_UNITE_INTERVAL);
|
|
||||||
if (shouldUnite)
|
|
||||||
luts = now;
|
|
||||||
}
|
|
||||||
RR->cluster->sendViaCluster(source,destination,packet.data(),packet.size(),shouldUnite);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
relayTo = RR->topology->getBestRoot(&source,1,true);
|
|
||||||
if (relayTo)
|
|
||||||
relayTo->send(packet.data(),packet.size(),now);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet.source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
|
|
||||||
}
|
|
||||||
} else if ((reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED) != 0) {
|
|
||||||
// Packet is the head of a fragmented packet series
|
|
||||||
|
|
||||||
Mutex::Lock _l(_rxQueue_m);
|
Mutex::Lock _l(_rxQueue_m);
|
||||||
RXQueueEntry *const rq = _findRXQueueEntry(now,packetId);
|
RXQueueEntry *const rq = _findRXQueueEntry(now,packetId);
|
||||||
@@ -248,7 +306,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
|||||||
|
|
||||||
rq->timestamp = now;
|
rq->timestamp = now;
|
||||||
rq->packetId = packetId;
|
rq->packetId = packetId;
|
||||||
rq->frag0.init(data,len,localAddr,fromAddr,now);
|
rq->frag0.init(data,len,path,now);
|
||||||
rq->totalFragments = 0;
|
rq->totalFragments = 0;
|
||||||
rq->haveFragments = 1;
|
rq->haveFragments = 1;
|
||||||
rq->complete = false;
|
rq->complete = false;
|
||||||
@@ -259,24 +317,24 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
|||||||
// We have all fragments -- assemble and process full Packet
|
// We have all fragments -- assemble and process full Packet
|
||||||
//TRACE("packet %.16llx is complete, assembling and processing...",pid);
|
//TRACE("packet %.16llx is complete, assembling and processing...",pid);
|
||||||
|
|
||||||
rq->frag0.init(data,len,localAddr,fromAddr,now);
|
rq->frag0.init(data,len,path,now);
|
||||||
for(unsigned int f=1;f<rq->totalFragments;++f)
|
for(unsigned int f=1;f<rq->totalFragments;++f)
|
||||||
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
|
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
|
||||||
|
|
||||||
if (rq->frag0.tryDecode(RR,false)) {
|
if (rq->frag0.tryDecode(RR)) {
|
||||||
rq->timestamp = 0; // packet decoded, free entry
|
rq->timestamp = 0; // packet decoded, free entry
|
||||||
} else {
|
} else {
|
||||||
rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
|
rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Still waiting on more fragments, but keep the head
|
// Still waiting on more fragments, but keep the head
|
||||||
rq->frag0.init(data,len,localAddr,fromAddr,now);
|
rq->frag0.init(data,len,path,now);
|
||||||
}
|
}
|
||||||
} // else this is a duplicate head, ignore
|
} // else this is a duplicate head, ignore
|
||||||
} else {
|
} else {
|
||||||
// Packet is unfragmented, so just process it
|
// Packet is unfragmented, so just process it
|
||||||
IncomingPacket packet(data,len,localAddr,fromAddr,now);
|
IncomingPacket packet(data,len,path,now);
|
||||||
if (!packet.tryDecode(RR,false)) {
|
if (!packet.tryDecode(RR)) {
|
||||||
Mutex::Lock _l(_rxQueue_m);
|
Mutex::Lock _l(_rxQueue_m);
|
||||||
RXQueueEntry *rq = &(_rxQueue[ZT_RX_QUEUE_SIZE - 1]);
|
RXQueueEntry *rq = &(_rxQueue[ZT_RX_QUEUE_SIZE - 1]);
|
||||||
unsigned long i = ZT_RX_QUEUE_SIZE - 1;
|
unsigned long i = ZT_RX_QUEUE_SIZE - 1;
|
||||||
@@ -286,7 +344,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
|||||||
rq = tmp;
|
rq = tmp;
|
||||||
}
|
}
|
||||||
rq->timestamp = now;
|
rq->timestamp = now;
|
||||||
rq->packetId = packetId;
|
rq->packetId = packet.packetId();
|
||||||
rq->frag0 = packet;
|
rq->frag0 = packet;
|
||||||
rq->totalFragments = 1;
|
rq->totalFragments = 1;
|
||||||
rq->haveFragments = 1;
|
rq->haveFragments = 1;
|
||||||
@@ -309,29 +367,17 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
|||||||
if (!network->hasConfig())
|
if (!network->hasConfig())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Sanity check -- bridge loop? OS problem?
|
|
||||||
if (to == network->mac())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Check to make sure this protocol is allowed on this network
|
|
||||||
if (!network->config().permitsEtherType(etherType)) {
|
|
||||||
TRACE("%.16llx: ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),(unsigned long long)network->id());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this packet is from someone other than the tap -- i.e. bridged in
|
// Check if this packet is from someone other than the tap -- i.e. bridged in
|
||||||
bool fromBridged = false;
|
bool fromBridged;
|
||||||
if (from != network->mac()) {
|
if ((fromBridged = (from != network->mac()))) {
|
||||||
if (!network->config().permitsBridging(RR->identity.address())) {
|
if (!network->config().permitsBridging(RR->identity.address())) {
|
||||||
TRACE("%.16llx: %s -> %s %s not forwarded, bridging disabled or this peer not a bridge",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
TRACE("%.16llx: %s -> %s %s not forwarded, bridging disabled or this peer not a bridge",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fromBridged = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to.isMulticast()) {
|
if (to.isMulticast()) {
|
||||||
// Destination is a multicast address (including broadcast)
|
MulticastGroup multicastGroup(to,0);
|
||||||
MulticastGroup mg(to,0);
|
|
||||||
|
|
||||||
if (to.isBroadcast()) {
|
if (to.isBroadcast()) {
|
||||||
if ( (etherType == ZT_ETHERTYPE_ARP) && (len >= 28) && ((((const uint8_t *)data)[2] == 0x08)&&(((const uint8_t *)data)[3] == 0x00)&&(((const uint8_t *)data)[4] == 6)&&(((const uint8_t *)data)[5] == 4)&&(((const uint8_t *)data)[7] == 0x01)) ) {
|
if ( (etherType == ZT_ETHERTYPE_ARP) && (len >= 28) && ((((const uint8_t *)data)[2] == 0x08)&&(((const uint8_t *)data)[3] == 0x00)&&(((const uint8_t *)data)[4] == 6)&&(((const uint8_t *)data)[5] == 4)&&(((const uint8_t *)data)[7] == 0x01)) ) {
|
||||||
@@ -344,7 +390,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
|||||||
* them into multicasts by stuffing the IP address being queried into
|
* them into multicasts by stuffing the IP address being queried into
|
||||||
* the 32-bit ADI field. In practice this uses our multicast pub/sub
|
* the 32-bit ADI field. In practice this uses our multicast pub/sub
|
||||||
* system to implement a kind of extended/distributed ARP table. */
|
* system to implement a kind of extended/distributed ARP table. */
|
||||||
mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
|
multicastGroup = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
|
||||||
} else if (!network->config().enableBroadcast()) {
|
} else if (!network->config().enableBroadcast()) {
|
||||||
// Don't transmit broadcasts if this network doesn't want them
|
// Don't transmit broadcasts if this network doesn't want them
|
||||||
TRACE("%.16llx: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled",network->id());
|
TRACE("%.16llx: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled",network->id());
|
||||||
@@ -434,68 +480,85 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
|||||||
} // else no NDP emulation
|
} // else no NDP emulation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check this after NDP emulation, since that has to be allowed in exactly this case
|
||||||
|
if (network->config().multicastLimit == 0) {
|
||||||
|
TRACE("%.16llx: dropped multicast: not allowed on network",network->id());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Learn multicast groups for bridged-in hosts.
|
/* Learn multicast groups for bridged-in hosts.
|
||||||
* Note that some OSes, most notably Linux, do this for you by learning
|
* Note that some OSes, most notably Linux, do this for you by learning
|
||||||
* multicast addresses on bridge interfaces and subscribing each slave.
|
* multicast addresses on bridge interfaces and subscribing each slave.
|
||||||
* But in that case this does no harm, as the sets are just merged. */
|
* But in that case this does no harm, as the sets are just merged. */
|
||||||
if (fromBridged)
|
if (fromBridged)
|
||||||
network->learnBridgedMulticastGroup(mg,RR->node->now());
|
network->learnBridgedMulticastGroup(multicastGroup,RR->node->now());
|
||||||
|
|
||||||
//TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),mg.toString().c_str(),etherTypeName(etherType),len);
|
//TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),multicastGroup.toString().c_str(),etherTypeName(etherType),len);
|
||||||
|
|
||||||
|
// First pass sets noTee to false, but noTee is set to true in OutboundMulticast to prevent duplicates.
|
||||||
|
if (!network->filterOutgoingPacket(false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
|
||||||
|
TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
RR->mc->send(
|
RR->mc->send(
|
||||||
((!network->config().isPublic())&&(network->config().com)) ? &(network->config().com) : (const CertificateOfMembership *)0,
|
|
||||||
network->config().multicastLimit,
|
network->config().multicastLimit,
|
||||||
RR->node->now(),
|
RR->node->now(),
|
||||||
network->id(),
|
network->id(),
|
||||||
|
network->config().disableCompression(),
|
||||||
network->config().activeBridges(),
|
network->config().activeBridges(),
|
||||||
mg,
|
multicastGroup,
|
||||||
(fromBridged) ? from : MAC(),
|
(fromBridged) ? from : MAC(),
|
||||||
etherType,
|
etherType,
|
||||||
data,
|
data,
|
||||||
len);
|
len);
|
||||||
|
} else if (to == network->mac()) {
|
||||||
return;
|
// Destination is this node, so just reinject it
|
||||||
}
|
RR->node->putFrame(network->id(),network->userPtr(),from,to,etherType,vlanId,data,len);
|
||||||
|
} else if (to[0] == MAC::firstOctetForNetwork(network->id())) {
|
||||||
if (to[0] == MAC::firstOctetForNetwork(network->id())) {
|
|
||||||
// Destination is another ZeroTier peer on the same network
|
// Destination is another ZeroTier peer on the same network
|
||||||
|
|
||||||
Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this
|
Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this
|
||||||
SharedPtr<Peer> toPeer(RR->topology->getPeer(toZT));
|
SharedPtr<Peer> toPeer(RR->topology->getPeer(toZT));
|
||||||
const bool includeCom = ( (network->config().isPrivate()) && (network->config().com) && ((!toPeer)||(toPeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) );
|
|
||||||
if ((fromBridged)||(includeCom)) {
|
if (!network->filterOutgoingPacket(false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId)) {
|
||||||
|
TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromBridged) {
|
||||||
Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME);
|
Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME);
|
||||||
outp.append(network->id());
|
outp.append(network->id());
|
||||||
if (includeCom) {
|
|
||||||
outp.append((unsigned char)0x01); // 0x01 -- COM included
|
|
||||||
network->config().com.serialize(outp);
|
|
||||||
} else {
|
|
||||||
outp.append((unsigned char)0x00);
|
outp.append((unsigned char)0x00);
|
||||||
}
|
|
||||||
to.appendTo(outp);
|
to.appendTo(outp);
|
||||||
from.appendTo(outp);
|
from.appendTo(outp);
|
||||||
outp.append((uint16_t)etherType);
|
outp.append((uint16_t)etherType);
|
||||||
outp.append(data,len);
|
outp.append(data,len);
|
||||||
|
if (!network->config().disableCompression())
|
||||||
outp.compress();
|
outp.compress();
|
||||||
send(outp,true,network->id());
|
send(outp,true);
|
||||||
} else {
|
} else {
|
||||||
Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME);
|
Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME);
|
||||||
outp.append(network->id());
|
outp.append(network->id());
|
||||||
outp.append((uint16_t)etherType);
|
outp.append((uint16_t)etherType);
|
||||||
outp.append(data,len);
|
outp.append(data,len);
|
||||||
|
if (!network->config().disableCompression())
|
||||||
outp.compress();
|
outp.compress();
|
||||||
send(outp,true,network->id());
|
send(outp,true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom);
|
//TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom);
|
||||||
|
} else {
|
||||||
|
// Destination is bridged behind a remote peer
|
||||||
|
|
||||||
|
// We filter with a NULL destination ZeroTier address first. Filtrations
|
||||||
|
// for each ZT destination are also done below. This is the same rationale
|
||||||
|
// and design as for multicast.
|
||||||
|
if (!network->filterOutgoingPacket(false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
|
||||||
|
TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
// Destination is bridged behind a remote peer
|
|
||||||
|
|
||||||
Address bridges[ZT_MAX_BRIDGE_SPAM];
|
Address bridges[ZT_MAX_BRIDGE_SPAM];
|
||||||
unsigned int numBridges = 0;
|
unsigned int numBridges = 0;
|
||||||
|
|
||||||
@@ -529,117 +592,34 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(unsigned int b=0;b<numBridges;++b) {
|
for(unsigned int b=0;b<numBridges;++b) {
|
||||||
SharedPtr<Peer> bridgePeer(RR->topology->getPeer(bridges[b]));
|
if (network->filterOutgoingPacket(true,RR->identity.address(),bridges[b],from,to,(const uint8_t *)data,len,etherType,vlanId)) {
|
||||||
Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME);
|
Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME);
|
||||||
outp.append(network->id());
|
outp.append(network->id());
|
||||||
if ( (network->config().isPrivate()) && (network->config().com) && ((!bridgePeer)||(bridgePeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) ) {
|
outp.append((uint8_t)0x00);
|
||||||
outp.append((unsigned char)0x01); // 0x01 -- COM included
|
|
||||||
network->config().com.serialize(outp);
|
|
||||||
} else {
|
|
||||||
outp.append((unsigned char)0);
|
|
||||||
}
|
|
||||||
to.appendTo(outp);
|
to.appendTo(outp);
|
||||||
from.appendTo(outp);
|
from.appendTo(outp);
|
||||||
outp.append((uint16_t)etherType);
|
outp.append((uint16_t)etherType);
|
||||||
outp.append(data,len);
|
outp.append(data,len);
|
||||||
|
if (!network->config().disableCompression())
|
||||||
outp.compress();
|
outp.compress();
|
||||||
send(outp,true,network->id());
|
send(outp,true);
|
||||||
|
} else {
|
||||||
|
TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Switch::send(const Packet &packet,bool encrypt,uint64_t nwid)
|
void Switch::send(Packet &packet,bool encrypt)
|
||||||
{
|
{
|
||||||
if (packet.destination() == RR->identity.address()) {
|
if (packet.destination() == RR->identity.address()) {
|
||||||
TRACE("BUG: caught attempt to send() to self, ignored");
|
TRACE("BUG: caught attempt to send() to self, ignored");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TRACE(">> %s to %s (%u bytes, encrypt==%d, nwid==%.16llx)",Packet::verbString(packet.verb()),packet.destination().toString().c_str(),packet.size(),(int)encrypt,nwid);
|
if (!_trySend(packet,encrypt)) {
|
||||||
|
|
||||||
if (!_trySend(packet,encrypt,nwid)) {
|
|
||||||
Mutex::Lock _l(_txQueue_m);
|
Mutex::Lock _l(_txQueue_m);
|
||||||
_txQueue.push_back(TXQueueEntry(packet.destination(),RR->node->now(),packet,encrypt,nwid));
|
_txQueue.push_back(TXQueueEntry(packet.destination(),RR->node->now(),packet,encrypt));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Switch::unite(const Address &p1,const Address &p2)
|
|
||||||
{
|
|
||||||
if ((p1 == RR->identity.address())||(p2 == RR->identity.address()))
|
|
||||||
return false;
|
|
||||||
SharedPtr<Peer> p1p = RR->topology->getPeer(p1);
|
|
||||||
if (!p1p)
|
|
||||||
return false;
|
|
||||||
SharedPtr<Peer> p2p = RR->topology->getPeer(p2);
|
|
||||||
if (!p2p)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const uint64_t now = RR->node->now();
|
|
||||||
|
|
||||||
std::pair<InetAddress,InetAddress> cg(Peer::findCommonGround(*p1p,*p2p,now));
|
|
||||||
if ((!(cg.first))||(cg.first.ipScope() != cg.second.ipScope()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
TRACE("unite: %s(%s) <> %s(%s)",p1.toString().c_str(),cg.second.toString().c_str(),p2.toString().c_str(),cg.first.toString().c_str());
|
|
||||||
|
|
||||||
/* Tell P1 where to find P2 and vice versa, sending the packets to P1 and
|
|
||||||
* P2 in randomized order in terms of which gets sent first. This is done
|
|
||||||
* since in a few cases NAT-t can be sensitive to slight timing differences
|
|
||||||
* in terms of when the two peers initiate. Normally this is accounted for
|
|
||||||
* by the nearly-simultaneous RENDEZVOUS kickoff from the relay, but
|
|
||||||
* given that relay are hosted on cloud providers this can in some
|
|
||||||
* cases have a few ms of latency between packet departures. By randomizing
|
|
||||||
* the order we make each attempted NAT-t favor one or the other going
|
|
||||||
* first, meaning if it doesn't succeed the first time it might the second
|
|
||||||
* and so forth. */
|
|
||||||
unsigned int alt = (unsigned int)RR->node->prng() & 1;
|
|
||||||
unsigned int completed = alt + 2;
|
|
||||||
while (alt != completed) {
|
|
||||||
if ((alt & 1) == 0) {
|
|
||||||
// Tell p1 where to find p2.
|
|
||||||
Packet outp(p1,RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
|
||||||
outp.append((unsigned char)0);
|
|
||||||
p2.appendTo(outp);
|
|
||||||
outp.append((uint16_t)cg.first.port());
|
|
||||||
if (cg.first.isV6()) {
|
|
||||||
outp.append((unsigned char)16);
|
|
||||||
outp.append(cg.first.rawIpData(),16);
|
|
||||||
} else {
|
|
||||||
outp.append((unsigned char)4);
|
|
||||||
outp.append(cg.first.rawIpData(),4);
|
|
||||||
}
|
|
||||||
outp.armor(p1p->key(),true);
|
|
||||||
p1p->send(outp.data(),outp.size(),now);
|
|
||||||
} else {
|
|
||||||
// Tell p2 where to find p1.
|
|
||||||
Packet outp(p2,RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
|
||||||
outp.append((unsigned char)0);
|
|
||||||
p1.appendTo(outp);
|
|
||||||
outp.append((uint16_t)cg.second.port());
|
|
||||||
if (cg.second.isV6()) {
|
|
||||||
outp.append((unsigned char)16);
|
|
||||||
outp.append(cg.second.rawIpData(),16);
|
|
||||||
} else {
|
|
||||||
outp.append((unsigned char)4);
|
|
||||||
outp.append(cg.second.rawIpData(),4);
|
|
||||||
}
|
|
||||||
outp.armor(p2p->key(),true);
|
|
||||||
p2p->send(outp.data(),outp.size(),now);
|
|
||||||
}
|
|
||||||
++alt; // counts up and also flips LSB
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Switch::rendezvous(const SharedPtr<Peer> &peer,const InetAddress &localAddr,const InetAddress &atAddr)
|
|
||||||
{
|
|
||||||
TRACE("sending NAT-t message to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str());
|
|
||||||
const uint64_t now = RR->node->now();
|
|
||||||
peer->sendHELLO(localAddr,atAddr,now,2); // first attempt: send low-TTL packet to 'open' local NAT
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_contactQueue_m);
|
|
||||||
_contactQueue.push_back(ContactQueueEntry(peer,now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY,localAddr,atAddr));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,7 +653,7 @@ void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
|
|||||||
while (i) {
|
while (i) {
|
||||||
RXQueueEntry *rq = &(_rxQueue[--i]);
|
RXQueueEntry *rq = &(_rxQueue[--i]);
|
||||||
if ((rq->timestamp)&&(rq->complete)) {
|
if ((rq->timestamp)&&(rq->complete)) {
|
||||||
if (rq->frag0.tryDecode(RR,false))
|
if (rq->frag0.tryDecode(RR))
|
||||||
rq->timestamp = 0;
|
rq->timestamp = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -683,7 +663,7 @@ void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
|
|||||||
Mutex::Lock _l(_txQueue_m);
|
Mutex::Lock _l(_txQueue_m);
|
||||||
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
|
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
|
||||||
if (txi->dest == peer->address()) {
|
if (txi->dest == peer->address()) {
|
||||||
if (_trySend(txi->packet,txi->encrypt,txi->nwid))
|
if (_trySend(txi->packet,txi->encrypt))
|
||||||
_txQueue.erase(txi++);
|
_txQueue.erase(txi++);
|
||||||
else ++txi;
|
else ++txi;
|
||||||
} else ++txi;
|
} else ++txi;
|
||||||
@@ -695,42 +675,6 @@ unsigned long Switch::doTimerTasks(uint64_t now)
|
|||||||
{
|
{
|
||||||
unsigned long nextDelay = 0xffffffff; // ceiling delay, caller will cap to minimum
|
unsigned long nextDelay = 0xffffffff; // ceiling delay, caller will cap to minimum
|
||||||
|
|
||||||
{ // Iterate through NAT traversal strategies for entries in contact queue
|
|
||||||
Mutex::Lock _l(_contactQueue_m);
|
|
||||||
for(std::list<ContactQueueEntry>::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) {
|
|
||||||
if (now >= qi->fireAtTime) {
|
|
||||||
if (!qi->peer->pushDirectPaths(qi->localAddr,qi->inaddr,now,true,false))
|
|
||||||
qi->peer->sendHELLO(qi->localAddr,qi->inaddr,now);
|
|
||||||
_contactQueue.erase(qi++);
|
|
||||||
continue;
|
|
||||||
/* Old symmetric NAT buster code, obsoleted by port prediction alg in SelfAwareness but left around for now in case we revert
|
|
||||||
if (qi->strategyIteration == 0) {
|
|
||||||
// First strategy: send packet directly to destination
|
|
||||||
qi->peer->sendHELLO(qi->localAddr,qi->inaddr,now);
|
|
||||||
} else if (qi->strategyIteration <= 3) {
|
|
||||||
// Strategies 1-3: try escalating ports for symmetric NATs that remap sequentially
|
|
||||||
InetAddress tmpaddr(qi->inaddr);
|
|
||||||
int p = (int)qi->inaddr.port() + qi->strategyIteration;
|
|
||||||
if (p > 65535)
|
|
||||||
p -= 64511;
|
|
||||||
tmpaddr.setPort((unsigned int)p);
|
|
||||||
qi->peer->sendHELLO(qi->localAddr,tmpaddr,now);
|
|
||||||
} else {
|
|
||||||
// All strategies tried, expire entry
|
|
||||||
_contactQueue.erase(qi++);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++qi->strategyIteration;
|
|
||||||
qi->fireAtTime = now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY;
|
|
||||||
nextDelay = std::min(nextDelay,(unsigned long)ZT_NAT_T_TACTICAL_ESCALATION_DELAY);
|
|
||||||
*/
|
|
||||||
} else {
|
|
||||||
nextDelay = std::min(nextDelay,(unsigned long)(qi->fireAtTime - now));
|
|
||||||
}
|
|
||||||
++qi; // if qi was erased, loop will have continued before here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Retry outstanding WHOIS requests
|
{ // Retry outstanding WHOIS requests
|
||||||
Mutex::Lock _l(_outstandingWhoisRequests_m);
|
Mutex::Lock _l(_outstandingWhoisRequests_m);
|
||||||
Hashtable< Address,WhoisRequest >::Iterator i(_outstandingWhoisRequests);
|
Hashtable< Address,WhoisRequest >::Iterator i(_outstandingWhoisRequests);
|
||||||
@@ -758,7 +702,7 @@ unsigned long Switch::doTimerTasks(uint64_t now)
|
|||||||
{ // Time out TX queue packets that never got WHOIS lookups or other info.
|
{ // Time out TX queue packets that never got WHOIS lookups or other info.
|
||||||
Mutex::Lock _l(_txQueue_m);
|
Mutex::Lock _l(_txQueue_m);
|
||||||
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
|
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
|
||||||
if (_trySend(txi->packet,txi->encrypt,txi->nwid))
|
if (_trySend(txi->packet,txi->encrypt))
|
||||||
_txQueue.erase(txi++);
|
_txQueue.erase(txi++);
|
||||||
else if ((now - txi->creationTime) > ZT_TRANSMIT_QUEUE_TIMEOUT) {
|
else if ((now - txi->creationTime) > ZT_TRANSMIT_QUEUE_TIMEOUT) {
|
||||||
TRACE("TX %s -> %s timed out",txi->packet.source().toString().c_str(),txi->packet.destination().toString().c_str());
|
TRACE("TX %s -> %s timed out",txi->packet.source().toString().c_str(),txi->packet.destination().toString().c_str());
|
||||||
@@ -781,106 +725,149 @@ unsigned long Switch::doTimerTasks(uint64_t now)
|
|||||||
return nextDelay;
|
return nextDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Switch::_shouldUnite(const uint64_t now,const Address &source,const Address &destination)
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_lastUniteAttempt_m);
|
||||||
|
uint64_t &ts = _lastUniteAttempt[_LastUniteKey(source,destination)];
|
||||||
|
if ((now - ts) >= ZT_MIN_UNITE_INTERVAL) {
|
||||||
|
ts = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
|
Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
|
||||||
{
|
{
|
||||||
SharedPtr<Peer> root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
|
SharedPtr<Peer> upstream(RR->topology->getUpstreamPeer(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
|
||||||
if (root) {
|
if (upstream) {
|
||||||
Packet outp(root->address(),RR->identity.address(),Packet::VERB_WHOIS);
|
Packet outp(upstream->address(),RR->identity.address(),Packet::VERB_WHOIS);
|
||||||
addr.appendTo(outp);
|
addr.appendTo(outp);
|
||||||
outp.armor(root->key(),true);
|
RR->node->expectReplyTo(outp.packetId());
|
||||||
if (root->send(outp.data(),outp.size(),RR->node->now()))
|
send(outp,true);
|
||||||
return root->address();
|
|
||||||
}
|
}
|
||||||
return Address();
|
return Address();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
|
bool Switch::_trySend(Packet &packet,bool encrypt)
|
||||||
{
|
{
|
||||||
SharedPtr<Peer> peer(RR->topology->getPeer(packet.destination()));
|
SharedPtr<Path> viaPath;
|
||||||
|
|
||||||
if (peer) {
|
|
||||||
const uint64_t now = RR->node->now();
|
const uint64_t now = RR->node->now();
|
||||||
|
const Address destination(packet.destination());
|
||||||
|
|
||||||
SharedPtr<Network> network;
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
if (nwid) {
|
uint64_t clusterMostRecentTs = 0;
|
||||||
network = RR->node->network(nwid);
|
int clusterMostRecentMemberId = -1;
|
||||||
if ((!network)||(!network->hasConfig()))
|
uint8_t clusterPeerSecret[ZT_PEER_SECRET_KEY_LENGTH];
|
||||||
return false; // we probably just left this network, let its packets die
|
if (RR->cluster)
|
||||||
}
|
clusterMostRecentMemberId = RR->cluster->checkSendViaCluster(destination,clusterMostRecentTs,clusterPeerSecret);
|
||||||
|
#endif
|
||||||
|
|
||||||
Path *viaPath = peer->getBestPath(now);
|
const SharedPtr<Peer> peer(RR->topology->getPeer(destination));
|
||||||
SharedPtr<Peer> relay;
|
if (peer) {
|
||||||
|
/* First get the best path, and if it's dead (and this is not a root)
|
||||||
|
* we attempt to re-activate that path but this packet will flow
|
||||||
|
* upstream. If the path comes back alive, it will be used in the future.
|
||||||
|
* For roots we don't do the alive check since roots are not required
|
||||||
|
* to send heartbeats "down" and because we have to at least try to
|
||||||
|
* go somewhere. */
|
||||||
|
|
||||||
if (!viaPath) {
|
viaPath = peer->getBestPath(now,false);
|
||||||
if (network) {
|
if ( (viaPath) && (!viaPath->alive(now)) && (!RR->topology->isUpstream(peer->identity())) ) {
|
||||||
unsigned int bestq = ~((unsigned int)0); // max unsigned int since quality is lower==better
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
unsigned int ptr = 0;
|
if ((clusterMostRecentMemberId < 0)||(viaPath->lastIn() > clusterMostRecentTs)) {
|
||||||
for(;;) {
|
#endif
|
||||||
const Address raddr(network->config().nextRelay(ptr));
|
if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) * 4,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL)) {
|
||||||
if (raddr) {
|
peer->attemptToContactAt(viaPath->localAddress(),viaPath->address(),now,false,viaPath->nextOutgoingCounter());
|
||||||
SharedPtr<Peer> rp(RR->topology->getPeer(raddr));
|
|
||||||
if (rp) {
|
|
||||||
const unsigned int q = rp->relayQuality(now);
|
|
||||||
if (q < bestq) {
|
|
||||||
bestq = q;
|
|
||||||
rp.swap(relay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!relay)
|
|
||||||
relay = RR->topology->getBestRoot();
|
|
||||||
|
|
||||||
if ( (!relay) || (!(viaPath = relay->getBestPath(now))) )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// viaPath will not be null if we make it here
|
|
||||||
|
|
||||||
// Push possible direct paths to us if we are relaying
|
|
||||||
if (relay) {
|
|
||||||
peer->pushDirectPaths(viaPath->localAddress(),viaPath->address(),now,false,( (network)&&(network->isAllowed(peer)) ));
|
|
||||||
viaPath->sent(now);
|
viaPath->sent(now);
|
||||||
}
|
}
|
||||||
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
Packet tmp(packet);
|
}
|
||||||
|
#endif
|
||||||
unsigned int chunkSize = std::min(tmp.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU);
|
viaPath.zero();
|
||||||
tmp.setFragmented(chunkSize < tmp.size());
|
|
||||||
|
|
||||||
const uint64_t trustedPathId = RR->topology->getOutboundPathTrust(viaPath->address());
|
|
||||||
if (trustedPathId) {
|
|
||||||
tmp.setTrusted(trustedPathId);
|
|
||||||
} else {
|
|
||||||
tmp.armor(peer->key(),encrypt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viaPath->send(RR,tmp.data(),chunkSize,now)) {
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
if (chunkSize < tmp.size()) {
|
if (clusterMostRecentMemberId >= 0) {
|
||||||
|
if ((viaPath)&&(viaPath->lastIn() < clusterMostRecentTs))
|
||||||
|
viaPath.zero();
|
||||||
|
} else if (!viaPath) {
|
||||||
|
#else
|
||||||
|
if (!viaPath) {
|
||||||
|
#endif
|
||||||
|
peer->tryMemorizedPath(now); // periodically attempt memorized or statically defined paths, if any are known
|
||||||
|
const SharedPtr<Peer> relay(RR->topology->getUpstreamPeer());
|
||||||
|
if ( (!relay) || (!(viaPath = relay->getBestPath(now,false))) ) {
|
||||||
|
if (!(viaPath = peer->getBestPath(now,true)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
if (clusterMostRecentMemberId < 0) {
|
||||||
|
#else
|
||||||
|
requestWhois(destination);
|
||||||
|
return false; // if we are not in cluster mode, there is no way we can send without knowing the peer directly
|
||||||
|
#endif
|
||||||
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int chunkSize = std::min(packet.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU);
|
||||||
|
packet.setFragmented(chunkSize < packet.size());
|
||||||
|
|
||||||
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
const uint64_t trustedPathId = (viaPath) ? RR->topology->getOutboundPathTrust(viaPath->address()) : 0;
|
||||||
|
if (trustedPathId) {
|
||||||
|
packet.setTrusted(trustedPathId);
|
||||||
|
} else {
|
||||||
|
packet.armor((clusterMostRecentMemberId >= 0) ? clusterPeerSecret : peer->key(),encrypt,(viaPath) ? viaPath->nextOutgoingCounter() : 0);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
const uint64_t trustedPathId = RR->topology->getOutboundPathTrust(viaPath->address());
|
||||||
|
if (trustedPathId) {
|
||||||
|
packet.setTrusted(trustedPathId);
|
||||||
|
} else {
|
||||||
|
packet.armor(peer->key(),encrypt,viaPath->nextOutgoingCounter());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
if ( ((viaPath)&&(viaPath->send(RR,packet.data(),chunkSize,now))) || ((clusterMostRecentMemberId >= 0)&&(RR->cluster->sendViaCluster(clusterMostRecentMemberId,destination,packet.data(),chunkSize))) ) {
|
||||||
|
#else
|
||||||
|
if (viaPath->send(RR,packet.data(),chunkSize,now)) {
|
||||||
|
#endif
|
||||||
|
if (chunkSize < packet.size()) {
|
||||||
// Too big for one packet, fragment the rest
|
// Too big for one packet, fragment the rest
|
||||||
unsigned int fragStart = chunkSize;
|
unsigned int fragStart = chunkSize;
|
||||||
unsigned int remaining = tmp.size() - chunkSize;
|
unsigned int remaining = packet.size() - chunkSize;
|
||||||
unsigned int fragsRemaining = (remaining / (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
|
unsigned int fragsRemaining = (remaining / (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
|
||||||
if ((fragsRemaining * (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH)) < remaining)
|
if ((fragsRemaining * (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH)) < remaining)
|
||||||
++fragsRemaining;
|
++fragsRemaining;
|
||||||
unsigned int totalFragments = fragsRemaining + 1;
|
const unsigned int totalFragments = fragsRemaining + 1;
|
||||||
|
|
||||||
for(unsigned int fno=1;fno<totalFragments;++fno) {
|
for(unsigned int fno=1;fno<totalFragments;++fno) {
|
||||||
chunkSize = std::min(remaining,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
|
chunkSize = std::min(remaining,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
|
||||||
Packet::Fragment frag(tmp,fragStart,chunkSize,fno,totalFragments);
|
Packet::Fragment frag(packet,fragStart,chunkSize,fno,totalFragments);
|
||||||
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
if (viaPath)
|
||||||
viaPath->send(RR,frag.data(),frag.size(),now);
|
viaPath->send(RR,frag.data(),frag.size(),now);
|
||||||
|
else if (clusterMostRecentMemberId >= 0)
|
||||||
|
RR->cluster->sendViaCluster(clusterMostRecentMemberId,destination,frag.data(),frag.size());
|
||||||
|
#else
|
||||||
|
viaPath->send(RR,frag.data(),frag.size(),now);
|
||||||
|
#endif
|
||||||
fragStart += chunkSize;
|
fragStart += chunkSize;
|
||||||
remaining -= chunkSize;
|
remaining -= chunkSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
requestWhois(packet.destination());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ class Switch : NonCopyable
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Switch(const RuntimeEnvironment *renv);
|
Switch(const RuntimeEnvironment *renv);
|
||||||
~Switch();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a packet is received from the real network
|
* Called when a packet is received from the real network
|
||||||
@@ -92,35 +91,10 @@ public:
|
|||||||
* Needless to say, the packet's source must be this node. Otherwise it
|
* Needless to say, the packet's source must be this node. Otherwise it
|
||||||
* won't be encrypted right. (This is not used for relaying.)
|
* won't be encrypted right. (This is not used for relaying.)
|
||||||
*
|
*
|
||||||
* The network ID should only be specified for frames and other actual
|
* @param packet Packet to send (buffer may be modified)
|
||||||
* network traffic. Other traffic such as controller requests and regular
|
|
||||||
* protocol messages should specify zero.
|
|
||||||
*
|
|
||||||
* @param packet Packet to send
|
|
||||||
* @param encrypt Encrypt packet payload? (always true except for HELLO)
|
* @param encrypt Encrypt packet payload? (always true except for HELLO)
|
||||||
* @param nwid Related network ID or 0 if message is not in-network traffic
|
|
||||||
*/
|
*/
|
||||||
void send(const Packet &packet,bool encrypt,uint64_t nwid);
|
void send(Packet &packet,bool encrypt);
|
||||||
|
|
||||||
/**
|
|
||||||
* Send RENDEZVOUS to two peers to permit them to directly connect
|
|
||||||
*
|
|
||||||
* This only works if both peers are known, with known working direct
|
|
||||||
* links to this peer. The best link for each peer is sent to the other.
|
|
||||||
*
|
|
||||||
* @param p1 One of two peers (order doesn't matter)
|
|
||||||
* @param p2 Second of pair
|
|
||||||
*/
|
|
||||||
bool unite(const Address &p1,const Address &p2);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt NAT traversal to peer at a given physical address
|
|
||||||
*
|
|
||||||
* @param peer Peer to contact
|
|
||||||
* @param localAddr Local interface address
|
|
||||||
* @param atAddr Address of peer
|
|
||||||
*/
|
|
||||||
void rendezvous(const SharedPtr<Peer> &peer,const InetAddress &localAddr,const InetAddress &atAddr);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request WHOIS on a given address
|
* Request WHOIS on a given address
|
||||||
@@ -150,8 +124,9 @@ public:
|
|||||||
unsigned long doTimerTasks(uint64_t now);
|
unsigned long doTimerTasks(uint64_t now);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool _shouldUnite(const uint64_t now,const Address &source,const Address &destination);
|
||||||
Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
|
Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
|
||||||
bool _trySend(const Packet &packet,bool encrypt,uint64_t nwid);
|
bool _trySend(Packet &packet,bool encrypt); // packet is modified if return is true
|
||||||
|
|
||||||
const RuntimeEnvironment *const RR;
|
const RuntimeEnvironment *const RR;
|
||||||
uint64_t _lastBeaconResponse;
|
uint64_t _lastBeaconResponse;
|
||||||
@@ -205,16 +180,14 @@ private:
|
|||||||
struct TXQueueEntry
|
struct TXQueueEntry
|
||||||
{
|
{
|
||||||
TXQueueEntry() {}
|
TXQueueEntry() {}
|
||||||
TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc,uint64_t nw) :
|
TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc) :
|
||||||
dest(d),
|
dest(d),
|
||||||
creationTime(ct),
|
creationTime(ct),
|
||||||
nwid(nw),
|
|
||||||
packet(p),
|
packet(p),
|
||||||
encrypt(enc) {}
|
encrypt(enc) {}
|
||||||
|
|
||||||
Address dest;
|
Address dest;
|
||||||
uint64_t creationTime;
|
uint64_t creationTime;
|
||||||
uint64_t nwid;
|
|
||||||
Packet packet; // unencrypted/unMAC'd packet -- this is done at send time
|
Packet packet; // unencrypted/unMAC'd packet -- this is done at send time
|
||||||
bool encrypt;
|
bool encrypt;
|
||||||
};
|
};
|
||||||
@@ -241,26 +214,6 @@ private:
|
|||||||
};
|
};
|
||||||
Hashtable< _LastUniteKey,uint64_t > _lastUniteAttempt; // key is always sorted in ascending order, for set-like behavior
|
Hashtable< _LastUniteKey,uint64_t > _lastUniteAttempt; // key is always sorted in ascending order, for set-like behavior
|
||||||
Mutex _lastUniteAttempt_m;
|
Mutex _lastUniteAttempt_m;
|
||||||
|
|
||||||
// Active attempts to contact remote peers, including state of multi-phase NAT traversal
|
|
||||||
struct ContactQueueEntry
|
|
||||||
{
|
|
||||||
ContactQueueEntry() {}
|
|
||||||
ContactQueueEntry(const SharedPtr<Peer> &p,uint64_t ft,const InetAddress &laddr,const InetAddress &a) :
|
|
||||||
peer(p),
|
|
||||||
fireAtTime(ft),
|
|
||||||
inaddr(a),
|
|
||||||
localAddr(laddr),
|
|
||||||
strategyIteration(0) {}
|
|
||||||
|
|
||||||
SharedPtr<Peer> peer;
|
|
||||||
uint64_t fireAtTime;
|
|
||||||
InetAddress inaddr;
|
|
||||||
InetAddress localAddr;
|
|
||||||
unsigned int strategyIteration;
|
|
||||||
};
|
|
||||||
std::list<ContactQueueEntry> _contactQueue;
|
|
||||||
Mutex _contactQueue_m;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -23,22 +23,35 @@
|
|||||||
#include "Network.hpp"
|
#include "Network.hpp"
|
||||||
#include "NetworkConfig.hpp"
|
#include "NetworkConfig.hpp"
|
||||||
#include "Buffer.hpp"
|
#include "Buffer.hpp"
|
||||||
|
#include "Switch.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
// 2015-11-16 -- The Fabulous Four (should have named them after Beatles!)
|
/*
|
||||||
//#define ZT_DEFAULT_WORLD_LENGTH 494
|
* 2016-01-13 ZeroTier planet definition for the third planet of Sol:
|
||||||
//static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x51,0x11,0x70,0xb2,0xfb,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x80,0x31,0xa4,0x65,0x95,0x45,0x06,0x1c,0xfb,0xc2,0x4e,0x5d,0xe7,0x0a,0x40,0x7a,0x97,0xce,0x36,0xa2,0x3d,0x05,0xca,0x87,0xc7,0x59,0x27,0x5c,0x8b,0x0d,0x4c,0xb4,0xbb,0x26,0x2f,0x77,0x17,0x5e,0xb7,0x4d,0xb8,0xd3,0xb4,0xe9,0x23,0x5d,0xcc,0xa2,0x71,0xa8,0xdf,0xf1,0x23,0xa3,0xb2,0x66,0x74,0xea,0xe5,0xdc,0x8d,0xef,0xd3,0x0a,0xa9,0xac,0xcb,0xda,0x93,0xbd,0x6c,0xcd,0x43,0x1d,0xa7,0x98,0x6a,0xde,0x70,0xc0,0xc6,0x1c,0xaf,0xf0,0xfd,0x7f,0x8a,0xb9,0x76,0x13,0xe1,0xde,0x4f,0xf3,0xd6,0x13,0x04,0x7e,0x19,0x87,0x6a,0xba,0x00,0x2a,0x6e,0x2b,0x23,0x18,0x93,0x0f,0x60,0xeb,0x09,0x7f,0x70,0xd0,0xf4,0xb0,0x28,0xb2,0xcd,0x6d,0x3d,0x0c,0x63,0xc0,0x14,0xb9,0x03,0x9f,0xf3,0x53,0x90,0xe4,0x11,0x81,0xf2,0x16,0xfb,0x2e,0x6f,0xa8,0xd9,0x5c,0x1e,0xe9,0x66,0x71,0x56,0x41,0x19,0x05,0xc3,0xdc,0xcf,0xea,0x78,0xd8,0xc6,0xdf,0xaf,0xba,0x68,0x81,0x70,0xb3,0xfa,0x00,0x01,0x04,0xc6,0xc7,0x61,0xdc,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x01,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x8a,0xcf,0x05,0x9f,0xe3,0x00,0x48,0x2f,0x6e,0xe5,0xdf,0xe9,0x02,0x31,0x9b,0x41,0x9d,0xe5,0xbd,0xc7,0x65,0x20,0x9c,0x0e,0xcd,0xa3,0x8c,0x4d,0x6e,0x4f,0xcf,0x0d,0x33,0x65,0x83,0x98,0xb4,0x52,0x7d,0xcd,0x22,0xf9,0x31,0x12,0xfb,0x9b,0xef,0xd0,0x2f,0xd7,0x8b,0xf7,0x26,0x1b,0x33,0x3f,0xc1,0x05,0xd1,0x92,0xa6,0x23,0xca,0x9e,0x50,0xfc,0x60,0xb3,0x74,0xa5,0x00,0x01,0x04,0xa2,0xf3,0x4d,0x6f,0x27,0x09,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x01,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09};
|
*
|
||||||
|
* There are two roots, each of which is a cluster spread across multiple
|
||||||
// 2015-11-20 -- Alice and Bob are live, and we're now IPv6 dual-stack!
|
* continents and providers. They are named Alice and Bob after the
|
||||||
//#define ZT_DEFAULT_WORLD_LENGTH 792
|
* canonical example names used in cryptography.
|
||||||
//static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x51,0x26,0x6f,0x7c,0x8a,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0xe8,0x0a,0xf5,0xbc,0xf8,0x3d,0x97,0xcd,0xc3,0xf8,0xe2,0x41,0x16,0x42,0x0f,0xc7,0x76,0x8e,0x07,0xf3,0x7e,0x9e,0x7d,0x1b,0xb3,0x23,0x21,0x79,0xce,0xb9,0xd0,0xcb,0xb5,0x94,0x7b,0x89,0x21,0x57,0x72,0xf6,0x70,0xa1,0xdd,0x67,0x38,0xcf,0x45,0x45,0xc2,0x8d,0x46,0xec,0x00,0x2c,0xe0,0x2a,0x63,0x3f,0x63,0x8d,0x33,0x08,0x51,0x07,0x77,0x81,0x5b,0x32,0x49,0xae,0x87,0x89,0xcf,0x31,0xaa,0x41,0xf1,0x52,0x97,0xdc,0xa2,0x55,0xe1,0x4a,0x6e,0x3c,0x04,0xf0,0x4f,0x8a,0x0e,0xe9,0xca,0xec,0x24,0x30,0x04,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x02,0x00,0xe0,0x01,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0xb7,0x40,0x01,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x6a,0x30,0x01,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09,0x7e,0x19,0x87,0x6a,0xba,0x00,0x2a,0x6e,0x2b,0x23,0x18,0x93,0x0f,0x60,0xeb,0x09,0x7f,0x70,0xd0,0xf4,0xb0,0x28,0xb2,0xcd,0x6d,0x3d,0x0c,0x63,0xc0,0x14,0xb9,0x03,0x9f,0xf3,0x53,0x90,0xe4,0x11,0x81,0xf2,0x16,0xfb,0x2e,0x6f,0xa8,0xd9,0x5c,0x1e,0xe9,0x66,0x71,0x56,0x41,0x19,0x05,0xc3,0xdc,0xcf,0xea,0x78,0xd8,0xc6,0xdf,0xaf,0xba,0x68,0x81,0x70,0xb3,0xfa,0x00,0x01,0x04,0xc6,0xc7,0x61,0xdc,0x27,0x09,0x8a,0xcf,0x05,0x9f,0xe3,0x00,0x48,0x2f,0x6e,0xe5,0xdf,0xe9,0x02,0x31,0x9b,0x41,0x9d,0xe5,0xbd,0xc7,0x65,0x20,0x9c,0x0e,0xcd,0xa3,0x8c,0x4d,0x6e,0x4f,0xcf,0x0d,0x33,0x65,0x83,0x98,0xb4,0x52,0x7d,0xcd,0x22,0xf9,0x31,0x12,0xfb,0x9b,0xef,0xd0,0x2f,0xd7,0x8b,0xf7,0x26,0x1b,0x33,0x3f,0xc1,0x05,0xd1,0x92,0xa6,0x23,0xca,0x9e,0x50,0xfc,0x60,0xb3,0x74,0xa5,0x00,0x01,0x04,0xa2,0xf3,0x4d,0x6f,0x27,0x09};
|
*
|
||||||
|
* Alice:
|
||||||
// 2015-12-17 -- Old New York root is dead, old SF still alive
|
*
|
||||||
//#define ZT_DEFAULT_WORLD_LENGTH 732
|
* root-alice-ams-01: Amsterdam, Netherlands
|
||||||
//static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x51,0xb1,0x7e,0x39,0x9d,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x8a,0xca,0xf2,0x3d,0x71,0x2e,0xc2,0x39,0x45,0x66,0xb3,0xe9,0x39,0x79,0xb1,0x55,0xc4,0xa9,0xfc,0xbc,0xfc,0x55,0xaf,0x8a,0x2f,0x38,0xc8,0xcd,0xe9,0x02,0x5b,0x86,0xa9,0x72,0xf7,0x16,0x00,0x35,0xb7,0x84,0xc9,0xfc,0xe4,0xfa,0x96,0x8b,0xf4,0x1e,0xba,0x60,0x9f,0x85,0x14,0xc2,0x07,0x4b,0xfd,0xd1,0x6c,0x19,0x69,0xd3,0xf9,0x09,0x9c,0x9d,0xe3,0xb9,0x8f,0x11,0x78,0x71,0xa7,0x4a,0x05,0xd8,0xcc,0x60,0xa2,0x06,0x66,0x9f,0x47,0xc2,0x71,0xb8,0x54,0x80,0x9c,0x45,0x16,0x10,0xa9,0xd0,0xbd,0xf7,0x03,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x02,0x00,0xe0,0x01,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0xb7,0x40,0x01,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x6a,0x30,0x01,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09,0x7e,0x19,0x87,0x6a,0xba,0x00,0x2a,0x6e,0x2b,0x23,0x18,0x93,0x0f,0x60,0xeb,0x09,0x7f,0x70,0xd0,0xf4,0xb0,0x28,0xb2,0xcd,0x6d,0x3d,0x0c,0x63,0xc0,0x14,0xb9,0x03,0x9f,0xf3,0x53,0x90,0xe4,0x11,0x81,0xf2,0x16,0xfb,0x2e,0x6f,0xa8,0xd9,0x5c,0x1e,0xe9,0x66,0x71,0x56,0x41,0x19,0x05,0xc3,0xdc,0xcf,0xea,0x78,0xd8,0xc6,0xdf,0xaf,0xba,0x68,0x81,0x70,0xb3,0xfa,0x00,0x02,0x04,0xc6,0xc7,0x61,0xdc,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0xc5,0xf0,0x01,0x27,0x09};
|
* root-alice-joh-01: Johannesburg, South Africa
|
||||||
|
* root-alice-nyc-01: New York, New York, USA
|
||||||
// 2016-01-13 -- Old San Francisco 1.0.1 root is dead, now we're just on Alice and Bob!
|
* root-alice-sao-01: Sao Paolo, Brazil
|
||||||
|
* root-alice-sfo-01: San Francisco, California, USA
|
||||||
|
* root-alice-sgp-01: Singapore
|
||||||
|
*
|
||||||
|
* Bob:
|
||||||
|
*
|
||||||
|
* root-bob-dfw-01: Dallas, Texas, USA
|
||||||
|
* root-bob-fra-01: Frankfurt, Germany
|
||||||
|
* root-bob-par-01: Paris, France
|
||||||
|
* root-bob-syd-01: Sydney, Australia
|
||||||
|
* root-bob-tok-01: Tokyo, Japan
|
||||||
|
* root-bob-tor-01: Toronto, Canada
|
||||||
|
*/
|
||||||
#define ZT_DEFAULT_WORLD_LENGTH 634
|
#define ZT_DEFAULT_WORLD_LENGTH 634
|
||||||
static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x52,0x3c,0x32,0x50,0x1a,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x4a,0xf7,0x86,0xa8,0x40,0xd6,0x52,0xea,0xae,0x9e,0x7a,0xbf,0x4c,0x97,0x66,0xab,0x2d,0x6f,0xaf,0xc9,0x2b,0x3a,0xff,0xed,0xd6,0x30,0x3e,0xc4,0x6a,0x65,0xf2,0xbd,0x83,0x52,0xf5,0x40,0xe9,0xcc,0x0d,0x6e,0x89,0x3f,0x9a,0xa0,0xb8,0xdf,0x42,0xd2,0x2f,0x84,0xe6,0x03,0x26,0x0f,0xa8,0xe3,0xcc,0x05,0x05,0x03,0xef,0x12,0x80,0x0d,0xce,0x3e,0xb6,0x58,0x3b,0x1f,0xa8,0xad,0xc7,0x25,0xf9,0x43,0x71,0xa7,0x5c,0x9a,0xc7,0xe1,0xa3,0xb8,0x88,0xd0,0x71,0x6c,0x94,0x99,0x73,0x41,0x0b,0x1b,0x48,0x84,0x02,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x02,0x00,0xe0,0x01,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0xb7,0x40,0x01,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x6a,0x30,0x01,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09};
|
static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x52,0x3c,0x32,0x50,0x1a,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x4a,0xf7,0x86,0xa8,0x40,0xd6,0x52,0xea,0xae,0x9e,0x7a,0xbf,0x4c,0x97,0x66,0xab,0x2d,0x6f,0xaf,0xc9,0x2b,0x3a,0xff,0xed,0xd6,0x30,0x3e,0xc4,0x6a,0x65,0xf2,0xbd,0x83,0x52,0xf5,0x40,0xe9,0xcc,0x0d,0x6e,0x89,0x3f,0x9a,0xa0,0xb8,0xdf,0x42,0xd2,0x2f,0x84,0xe6,0x03,0x26,0x0f,0xa8,0xe3,0xcc,0x05,0x05,0x03,0xef,0x12,0x80,0x0d,0xce,0x3e,0xb6,0x58,0x3b,0x1f,0xa8,0xad,0xc7,0x25,0xf9,0x43,0x71,0xa7,0x5c,0x9a,0xc7,0xe1,0xa3,0xb8,0x88,0xd0,0x71,0x6c,0x94,0x99,0x73,0x41,0x0b,0x1b,0x48,0x84,0x02,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x02,0x00,0xe0,0x01,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0xb7,0x40,0x01,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x6a,0x30,0x01,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09};
|
||||||
|
|
||||||
@@ -47,88 +60,22 @@ Topology::Topology(const RuntimeEnvironment *renv) :
|
|||||||
_trustedPathCount(0),
|
_trustedPathCount(0),
|
||||||
_amRoot(false)
|
_amRoot(false)
|
||||||
{
|
{
|
||||||
std::string alls(RR->node->dataStoreGet("peers.save"));
|
|
||||||
const uint8_t *all = reinterpret_cast<const uint8_t *>(alls.data());
|
|
||||||
RR->node->dataStoreDelete("peers.save");
|
|
||||||
|
|
||||||
Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE> *deserializeBuf = new Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE>();
|
|
||||||
unsigned int ptr = 0;
|
|
||||||
while ((ptr + 4) < alls.size()) {
|
|
||||||
try {
|
try {
|
||||||
const unsigned int reclen = ( // each Peer serialized record is prefixed by a record length
|
World cachedPlanet;
|
||||||
((((unsigned int)all[ptr]) & 0xff) << 24) |
|
std::string buf(RR->node->dataStoreGet("planet"));
|
||||||
((((unsigned int)all[ptr + 1]) & 0xff) << 16) |
|
if (buf.length() > 0) {
|
||||||
((((unsigned int)all[ptr + 2]) & 0xff) << 8) |
|
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp(buf.data(),(unsigned int)buf.length());
|
||||||
(((unsigned int)all[ptr + 3]) & 0xff)
|
cachedPlanet.deserialize(dswtmp,0);
|
||||||
);
|
|
||||||
unsigned int pos = 0;
|
|
||||||
deserializeBuf->copyFrom(all + ptr,reclen + 4);
|
|
||||||
SharedPtr<Peer> p(Peer::deserializeNew(RR,RR->identity,*deserializeBuf,pos));
|
|
||||||
ptr += pos;
|
|
||||||
if (!p)
|
|
||||||
break; // stop if invalid records
|
|
||||||
if (p->address() != RR->identity.address())
|
|
||||||
_peers.set(p->address(),p);
|
|
||||||
} catch ( ... ) {
|
|
||||||
break; // stop if invalid records
|
|
||||||
}
|
}
|
||||||
}
|
addWorld(cachedPlanet,false);
|
||||||
delete deserializeBuf;
|
} catch ( ... ) {}
|
||||||
|
|
||||||
clean(RR->node->now());
|
World defaultPlanet;
|
||||||
|
|
||||||
std::string dsWorld(RR->node->dataStoreGet("world"));
|
|
||||||
World cachedWorld;
|
|
||||||
if (dsWorld.length() > 0) {
|
|
||||||
try {
|
|
||||||
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp(dsWorld.data(),(unsigned int)dsWorld.length());
|
|
||||||
cachedWorld.deserialize(dswtmp,0);
|
|
||||||
} catch ( ... ) {
|
|
||||||
cachedWorld = World(); // clear if cached world is invalid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
World defaultWorld;
|
|
||||||
{
|
{
|
||||||
Buffer<ZT_DEFAULT_WORLD_LENGTH> wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH);
|
Buffer<ZT_DEFAULT_WORLD_LENGTH> wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH);
|
||||||
defaultWorld.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top
|
defaultPlanet.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top
|
||||||
}
|
|
||||||
if (cachedWorld.shouldBeReplacedBy(defaultWorld,false)) {
|
|
||||||
_setWorld(defaultWorld);
|
|
||||||
if (dsWorld.length() > 0)
|
|
||||||
RR->node->dataStoreDelete("world");
|
|
||||||
} else _setWorld(cachedWorld);
|
|
||||||
}
|
|
||||||
|
|
||||||
Topology::~Topology()
|
|
||||||
{
|
|
||||||
Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE> *pbuf = 0;
|
|
||||||
try {
|
|
||||||
pbuf = new Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE>();
|
|
||||||
std::string all;
|
|
||||||
|
|
||||||
Address *a = (Address *)0;
|
|
||||||
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
|
||||||
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
|
|
||||||
while (i.next(a,p)) {
|
|
||||||
if (std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end()) {
|
|
||||||
pbuf->clear();
|
|
||||||
try {
|
|
||||||
(*p)->serialize(*pbuf);
|
|
||||||
try {
|
|
||||||
all.append((const char *)pbuf->data(),pbuf->size());
|
|
||||||
} catch ( ... ) {
|
|
||||||
return; // out of memory? just skip
|
|
||||||
}
|
|
||||||
} catch ( ... ) {} // peer too big? shouldn't happen, but it so skip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RR->node->dataStorePut("peers.save",all,true);
|
|
||||||
|
|
||||||
delete pbuf;
|
|
||||||
} catch ( ... ) {
|
|
||||||
delete pbuf;
|
|
||||||
}
|
}
|
||||||
|
addWorld(defaultPlanet,false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
|
SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
|
||||||
@@ -144,14 +91,13 @@ SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
|
|||||||
|
|
||||||
SharedPtr<Peer> np;
|
SharedPtr<Peer> np;
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_peers_m);
|
||||||
SharedPtr<Peer> &hp = _peers[peer->address()];
|
SharedPtr<Peer> &hp = _peers[peer->address()];
|
||||||
if (!hp)
|
if (!hp)
|
||||||
hp = peer;
|
hp = peer;
|
||||||
np = hp;
|
np = hp;
|
||||||
}
|
}
|
||||||
|
|
||||||
np->use(RR->node->now());
|
|
||||||
saveIdentity(np->identity());
|
saveIdentity(np->identity());
|
||||||
|
|
||||||
return np;
|
return np;
|
||||||
@@ -165,39 +111,35 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_peers_m);
|
||||||
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
||||||
if (ap) {
|
if (ap)
|
||||||
(*ap)->use(RR->node->now());
|
|
||||||
return *ap;
|
return *ap;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Identity id(_getIdentity(zta));
|
Identity id(_getIdentity(zta));
|
||||||
if (id) {
|
if (id) {
|
||||||
SharedPtr<Peer> np(new Peer(RR,RR->identity,id));
|
SharedPtr<Peer> np(new Peer(RR,RR->identity,id));
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_peers_m);
|
||||||
SharedPtr<Peer> &ap = _peers[zta];
|
SharedPtr<Peer> &ap = _peers[zta];
|
||||||
if (!ap)
|
if (!ap)
|
||||||
ap.swap(np);
|
ap.swap(np);
|
||||||
ap->use(RR->node->now());
|
|
||||||
return ap;
|
return ap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {} // invalid identity on disk?
|
||||||
fprintf(stderr,"EXCEPTION in getPeer() part 2\n");
|
|
||||||
abort();
|
|
||||||
} // invalid identity on disk?
|
|
||||||
|
|
||||||
return SharedPtr<Peer>();
|
return SharedPtr<Peer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Identity Topology::getIdentity(const Address &zta)
|
Identity Topology::getIdentity(const Address &zta)
|
||||||
{
|
{
|
||||||
{
|
if (zta == RR->identity.address()) {
|
||||||
Mutex::Lock _l(_lock);
|
return RR->identity;
|
||||||
|
} else {
|
||||||
|
Mutex::Lock _l(_peers_m);
|
||||||
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
||||||
if (ap)
|
if (ap)
|
||||||
return (*ap)->identity();
|
return (*ap)->identity();
|
||||||
@@ -214,63 +156,61 @@ void Topology::saveIdentity(const Identity &id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid)
|
SharedPtr<Peer> Topology::getUpstreamPeer(const Address *avoid,unsigned int avoidCount,bool strictAvoid)
|
||||||
{
|
{
|
||||||
const uint64_t now = RR->node->now();
|
const uint64_t now = RR->node->now();
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l1(_peers_m);
|
||||||
|
Mutex::Lock _l2(_upstreams_m);
|
||||||
|
|
||||||
if (_amRoot) {
|
if (_amRoot) {
|
||||||
/* If I am a root server, the "best" root server is the one whose address
|
/* If I am a root, pick another root that isn't mine and that
|
||||||
* is numerically greater than mine (with wrap at top of list). This
|
* has a numerically greater ID. This causes packets to roam
|
||||||
* causes packets searching for a route to pretty much literally
|
* around the top rather than bouncing between just two. */
|
||||||
* circumnavigate the globe rather than bouncing between just two. */
|
|
||||||
|
|
||||||
for(unsigned long p=0;p<_rootAddresses.size();++p) {
|
for(unsigned long p=0;p<_upstreamAddresses.size();++p) {
|
||||||
if (_rootAddresses[p] == RR->identity.address()) {
|
if (_upstreamAddresses[p] == RR->identity.address()) {
|
||||||
for(unsigned long q=1;q<_rootAddresses.size();++q) {
|
for(unsigned long q=1;q<_upstreamAddresses.size();++q) {
|
||||||
const SharedPtr<Peer> *const nextsn = _peers.get(_rootAddresses[(p + q) % _rootAddresses.size()]);
|
const SharedPtr<Peer> *const nextsn = _peers.get(_upstreamAddresses[(p + q) % _upstreamAddresses.size()]);
|
||||||
if ((nextsn)&&((*nextsn)->hasActiveDirectPath(now))) {
|
if ((nextsn)&&((*nextsn)->hasActiveDirectPath(now)))
|
||||||
(*nextsn)->use(now);
|
|
||||||
return *nextsn;
|
return *nextsn;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* If I am not a root server, the best root server is the active one with
|
/* Otherwise pick the bestest looking upstream */
|
||||||
* the lowest quality score. (lower == better) */
|
|
||||||
|
|
||||||
unsigned int bestQualityOverall = ~((unsigned int)0);
|
unsigned int bestQualityOverall = ~((unsigned int)0);
|
||||||
unsigned int bestQualityNotAvoid = ~((unsigned int)0);
|
unsigned int bestQualityNotAvoid = ~((unsigned int)0);
|
||||||
const SharedPtr<Peer> *bestOverall = (const SharedPtr<Peer> *)0;
|
const SharedPtr<Peer> *bestOverall = (const SharedPtr<Peer> *)0;
|
||||||
const SharedPtr<Peer> *bestNotAvoid = (const SharedPtr<Peer> *)0;
|
const SharedPtr<Peer> *bestNotAvoid = (const SharedPtr<Peer> *)0;
|
||||||
|
|
||||||
for(std::vector< SharedPtr<Peer> >::const_iterator r(_rootPeers.begin());r!=_rootPeers.end();++r) {
|
for(std::vector<Address>::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
|
||||||
|
const SharedPtr<Peer> *p = _peers.get(*a);
|
||||||
|
if (p) {
|
||||||
bool avoiding = false;
|
bool avoiding = false;
|
||||||
for(unsigned int i=0;i<avoidCount;++i) {
|
for(unsigned int i=0;i<avoidCount;++i) {
|
||||||
if (avoid[i] == (*r)->address()) {
|
if (avoid[i] == (*p)->address()) {
|
||||||
avoiding = true;
|
avoiding = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const unsigned int q = (*r)->relayQuality(now);
|
const unsigned int q = (*p)->relayQuality(now);
|
||||||
if (q <= bestQualityOverall) {
|
if (q <= bestQualityOverall) {
|
||||||
bestQualityOverall = q;
|
bestQualityOverall = q;
|
||||||
bestOverall = &(*r);
|
bestOverall = &(*p);
|
||||||
}
|
}
|
||||||
if ((!avoiding)&&(q <= bestQualityNotAvoid)) {
|
if ((!avoiding)&&(q <= bestQualityNotAvoid)) {
|
||||||
bestQualityNotAvoid = q;
|
bestQualityNotAvoid = q;
|
||||||
bestNotAvoid = &(*r);
|
bestNotAvoid = &(*p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bestNotAvoid) {
|
if (bestNotAvoid) {
|
||||||
(*bestNotAvoid)->use(now);
|
|
||||||
return *bestNotAvoid;
|
return *bestNotAvoid;
|
||||||
} else if ((!strictAvoid)&&(bestOverall)) {
|
} else if ((!strictAvoid)&&(bestOverall)) {
|
||||||
(*bestOverall)->use(now);
|
|
||||||
return *bestOverall;
|
return *bestOverall;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,45 +221,217 @@ SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCou
|
|||||||
|
|
||||||
bool Topology::isUpstream(const Identity &id) const
|
bool Topology::isUpstream(const Identity &id) const
|
||||||
{
|
{
|
||||||
if (isRoot(id))
|
Mutex::Lock _l(_upstreams_m);
|
||||||
return true;
|
return (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),id.address()) != _upstreamAddresses.end());
|
||||||
std::vector< SharedPtr<Network> > nws(RR->node->allNetworks());
|
|
||||||
for(std::vector< SharedPtr<Network> >::const_iterator nw(nws.begin());nw!=nws.end();++nw) {
|
|
||||||
if ((*nw)->config().isRelay(id.address())) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Topology::shouldAcceptWorldUpdateFrom(const Address &addr) const
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_upstreams_m);
|
||||||
|
if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),addr) != _upstreamAddresses.end())
|
||||||
|
return true;
|
||||||
|
for(std::vector< std::pair< uint64_t,Address> >::const_iterator s(_moonSeeds.begin());s!=_moonSeeds.end();++s) {
|
||||||
|
if (s->second == addr)
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Topology::worldUpdateIfValid(const World &newWorld)
|
ZT_PeerRole Topology::role(const Address &ztaddr) const
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_upstreams_m);
|
||||||
if (_world.shouldBeReplacedBy(newWorld,true)) {
|
if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),ztaddr) != _upstreamAddresses.end()) {
|
||||||
_setWorld(newWorld);
|
for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
|
||||||
try {
|
if (i->identity.address() == ztaddr)
|
||||||
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp;
|
return ZT_PEER_ROLE_PLANET;
|
||||||
newWorld.serialize(dswtmp,false);
|
}
|
||||||
RR->node->dataStorePut("world",dswtmp.data(),dswtmp.size(),false);
|
return ZT_PEER_ROLE_MOON;
|
||||||
} catch ( ... ) {
|
}
|
||||||
RR->node->dataStoreDelete("world");
|
return ZT_PEER_ROLE_LEAF;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Topology::isProhibitedEndpoint(const Address &ztaddr,const InetAddress &ipaddr) const
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_upstreams_m);
|
||||||
|
|
||||||
|
// For roots the only permitted addresses are those defined. This adds just a little
|
||||||
|
// bit of extra security against spoofing, replaying, etc.
|
||||||
|
if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),ztaddr) != _upstreamAddresses.end()) {
|
||||||
|
for(std::vector<World::Root>::const_iterator r(_planet.roots().begin());r!=_planet.roots().end();++r) {
|
||||||
|
if (r->identity.address() == ztaddr) {
|
||||||
|
if (r->stableEndpoints.size() == 0)
|
||||||
|
return false; // no stable endpoints specified, so allow dynamic paths
|
||||||
|
for(std::vector<InetAddress>::const_iterator e(r->stableEndpoints.begin());e!=r->stableEndpoints.end();++e) {
|
||||||
|
if (ipaddr.ipsEqual(*e))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
|
||||||
|
for(std::vector<World::Root>::const_iterator r(m->roots().begin());r!=m->roots().end();++r) {
|
||||||
|
if (r->identity.address() == ztaddr) {
|
||||||
|
if (r->stableEndpoints.size() == 0)
|
||||||
|
return false; // no stable endpoints specified, so allow dynamic paths
|
||||||
|
for(std::vector<InetAddress>::const_iterator e(r->stableEndpoints.begin());e!=r->stableEndpoints.end();++e) {
|
||||||
|
if (ipaddr.ipsEqual(*e))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Topology::addWorld(const World &newWorld,bool alwaysAcceptNew)
|
||||||
|
{
|
||||||
|
if ((newWorld.type() != World::TYPE_PLANET)&&(newWorld.type() != World::TYPE_MOON))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Mutex::Lock _l1(_upstreams_m);
|
||||||
|
Mutex::Lock _l2(_peers_m);
|
||||||
|
|
||||||
|
World *existing = (World *)0;
|
||||||
|
switch(newWorld.type()) {
|
||||||
|
case World::TYPE_PLANET:
|
||||||
|
existing = &_planet;
|
||||||
|
break;
|
||||||
|
case World::TYPE_MOON:
|
||||||
|
for(std::vector< World >::iterator m(_moons.begin());m!=_moons.end();++m) {
|
||||||
|
if (m->id() == newWorld.id()) {
|
||||||
|
existing = &(*m);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
if (existing->shouldBeReplacedBy(newWorld))
|
||||||
|
*existing = newWorld;
|
||||||
|
else return false;
|
||||||
|
} else if (newWorld.type() == World::TYPE_MOON) {
|
||||||
|
if (alwaysAcceptNew) {
|
||||||
|
_moons.push_back(newWorld);
|
||||||
|
existing = &(_moons.back());
|
||||||
|
} else {
|
||||||
|
for(std::vector< std::pair<uint64_t,Address> >::iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) {
|
||||||
|
if (m->first == newWorld.id()) {
|
||||||
|
for(std::vector<World::Root>::const_iterator r(newWorld.roots().begin());r!=newWorld.roots().end();++r) {
|
||||||
|
if (r->identity.address() == m->second) {
|
||||||
|
_moonSeeds.erase(m);
|
||||||
|
_moons.push_back(newWorld);
|
||||||
|
existing = &(_moons.back());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (existing)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!existing)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char savePath[64];
|
||||||
|
if (existing->type() == World::TYPE_MOON) {
|
||||||
|
Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",existing->id());
|
||||||
|
} else {
|
||||||
|
Utils::scopy(savePath,sizeof(savePath),"planet");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp;
|
||||||
|
existing->serialize(dswtmp,false);
|
||||||
|
RR->node->dataStorePut(savePath,dswtmp.data(),dswtmp.size(),false);
|
||||||
|
} catch ( ... ) {
|
||||||
|
RR->node->dataStoreDelete(savePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
_memoizeUpstreams();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Topology::addMoon(const uint64_t id,const Address &seed)
|
||||||
|
{
|
||||||
|
char savePath[64];
|
||||||
|
Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",id);
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::string moonBin(RR->node->dataStoreGet(savePath));
|
||||||
|
if (moonBin.length() > 1) {
|
||||||
|
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> wtmp(moonBin.data(),(unsigned int)moonBin.length());
|
||||||
|
World w;
|
||||||
|
w.deserialize(wtmp);
|
||||||
|
if ((w.type() == World::TYPE_MOON)&&(w.id() == id)) {
|
||||||
|
addWorld(w,true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch ( ... ) {}
|
||||||
|
|
||||||
|
if (seed) {
|
||||||
|
Mutex::Lock _l(_upstreams_m);
|
||||||
|
if (std::find(_moonSeeds.begin(),_moonSeeds.end(),std::pair<uint64_t,Address>(id,seed)) == _moonSeeds.end())
|
||||||
|
_moonSeeds.push_back(std::pair<uint64_t,Address>(id,seed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Topology::removeMoon(const uint64_t id)
|
||||||
|
{
|
||||||
|
Mutex::Lock _l1(_upstreams_m);
|
||||||
|
Mutex::Lock _l2(_peers_m);
|
||||||
|
|
||||||
|
std::vector<World> nm;
|
||||||
|
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
|
||||||
|
if (m->id() != id) {
|
||||||
|
nm.push_back(*m);
|
||||||
|
} else {
|
||||||
|
char savePath[64];
|
||||||
|
Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",id);
|
||||||
|
RR->node->dataStoreDelete(savePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_moons.swap(nm);
|
||||||
|
|
||||||
|
std::vector< std::pair<uint64_t,Address> > cm;
|
||||||
|
for(std::vector< std::pair<uint64_t,Address> >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) {
|
||||||
|
if (m->first != id)
|
||||||
|
cm.push_back(*m);
|
||||||
|
}
|
||||||
|
_moonSeeds.swap(cm);
|
||||||
|
|
||||||
|
_memoizeUpstreams();
|
||||||
|
}
|
||||||
|
|
||||||
void Topology::clean(uint64_t now)
|
void Topology::clean(uint64_t now)
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
{
|
||||||
|
Mutex::Lock _l1(_peers_m);
|
||||||
|
Mutex::Lock _l2(_upstreams_m);
|
||||||
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
|
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
|
||||||
Address *a = (Address *)0;
|
Address *a = (Address *)0;
|
||||||
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
||||||
while (i.next(a,p)) {
|
while (i.next(a,p)) {
|
||||||
if (((now - (*p)->lastUsed()) >= ZT_PEER_IN_MEMORY_EXPIRATION)&&(std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end())) {
|
if ( (!(*p)->isAlive(now)) && (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),*a) == _upstreamAddresses.end()) )
|
||||||
_peers.erase(*a);
|
_peers.erase(*a);
|
||||||
} else {
|
}
|
||||||
(*p)->clean(now);
|
}
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_paths_m);
|
||||||
|
Hashtable< Path::HashKey,SharedPtr<Path> >::Iterator i(_paths);
|
||||||
|
Path::HashKey *k = (Path::HashKey *)0;
|
||||||
|
SharedPtr<Path> *p = (SharedPtr<Path> *)0;
|
||||||
|
while (i.next(k,p)) {
|
||||||
|
if (p->reclaimIfWeak())
|
||||||
|
_paths.erase(*k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,28 +449,48 @@ Identity Topology::_getIdentity(const Address &zta)
|
|||||||
return Identity();
|
return Identity();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Topology::_setWorld(const World &newWorld)
|
void Topology::_memoizeUpstreams()
|
||||||
{
|
{
|
||||||
// assumed _lock is locked (or in constructor)
|
// assumes _upstreams_m and _peers_m are locked
|
||||||
_world = newWorld;
|
_upstreamAddresses.clear();
|
||||||
_amRoot = false;
|
_amRoot = false;
|
||||||
_rootAddresses.clear();
|
|
||||||
_rootPeers.clear();
|
for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
|
||||||
for(std::vector<World::Root>::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) {
|
if (i->identity == RR->identity) {
|
||||||
_rootAddresses.push_back(r->identity.address());
|
|
||||||
if (r->identity.address() == RR->identity.address()) {
|
|
||||||
_amRoot = true;
|
_amRoot = true;
|
||||||
} else {
|
} else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),i->identity.address()) == _upstreamAddresses.end()) {
|
||||||
SharedPtr<Peer> *rp = _peers.get(r->identity.address());
|
_upstreamAddresses.push_back(i->identity.address());
|
||||||
if (rp) {
|
SharedPtr<Peer> &hp = _peers[i->identity.address()];
|
||||||
_rootPeers.push_back(*rp);
|
if (!hp) {
|
||||||
} else {
|
hp = new Peer(RR,RR->identity,i->identity);
|
||||||
SharedPtr<Peer> newrp(new Peer(RR,RR->identity,r->identity));
|
saveIdentity(i->identity);
|
||||||
_peers.set(r->identity.address(),newrp);
|
}
|
||||||
_rootPeers.push_back(newrp);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
|
||||||
|
for(std::vector<World::Root>::const_iterator i(m->roots().begin());i!=m->roots().end();++i) {
|
||||||
|
if (i->identity == RR->identity) {
|
||||||
|
_amRoot = true;
|
||||||
|
} else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),i->identity.address()) == _upstreamAddresses.end()) {
|
||||||
|
_upstreamAddresses.push_back(i->identity.address());
|
||||||
|
SharedPtr<Peer> &hp = _peers[i->identity.address()];
|
||||||
|
if (!hp) {
|
||||||
|
hp = new Peer(RR,RR->identity,i->identity);
|
||||||
|
saveIdentity(i->identity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::sort(_upstreamAddresses.begin(),_upstreamAddresses.end());
|
||||||
|
|
||||||
|
_cor.clear();
|
||||||
|
for(std::vector<Address>::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
|
||||||
|
if (!_cor.addRepresentative(*a))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_cor.sign(RR->identity,RR->node->now());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -33,10 +33,12 @@
|
|||||||
#include "Address.hpp"
|
#include "Address.hpp"
|
||||||
#include "Identity.hpp"
|
#include "Identity.hpp"
|
||||||
#include "Peer.hpp"
|
#include "Peer.hpp"
|
||||||
|
#include "Path.hpp"
|
||||||
#include "Mutex.hpp"
|
#include "Mutex.hpp"
|
||||||
#include "InetAddress.hpp"
|
#include "InetAddress.hpp"
|
||||||
#include "Hashtable.hpp"
|
#include "Hashtable.hpp"
|
||||||
#include "World.hpp"
|
#include "World.hpp"
|
||||||
|
#include "CertificateOfRepresentation.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
@@ -49,7 +51,6 @@ class Topology
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Topology(const RuntimeEnvironment *renv);
|
Topology(const RuntimeEnvironment *renv);
|
||||||
~Topology();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a peer to database
|
* Add a peer to database
|
||||||
@@ -82,13 +83,29 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline SharedPtr<Peer> getPeerNoCache(const Address &zta)
|
inline SharedPtr<Peer> getPeerNoCache(const Address &zta)
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_peers_m);
|
||||||
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
||||||
if (ap)
|
if (ap)
|
||||||
return *ap;
|
return *ap;
|
||||||
return SharedPtr<Peer>();
|
return SharedPtr<Peer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a Path object for a given local and remote physical address, creating if needed
|
||||||
|
*
|
||||||
|
* @param l Local address or NULL for 'any' or 'wildcard'
|
||||||
|
* @param r Remote address
|
||||||
|
* @return Pointer to canonicalized Path object
|
||||||
|
*/
|
||||||
|
inline SharedPtr<Path> getPath(const InetAddress &l,const InetAddress &r)
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_paths_m);
|
||||||
|
SharedPtr<Path> &p = _paths[Path::HashKey(l,r)];
|
||||||
|
if (!p)
|
||||||
|
p.setToUnsafe(new Path(l,r));
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the identity of a peer
|
* Get the identity of a peer
|
||||||
*
|
*
|
||||||
@@ -108,35 +125,21 @@ public:
|
|||||||
void saveIdentity(const Identity &id);
|
void saveIdentity(const Identity &id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current favorite root server
|
* Get the current best upstream peer
|
||||||
*
|
*
|
||||||
* @return Root server with lowest latency or NULL if none
|
* @return Root server with lowest latency or NULL if none
|
||||||
*/
|
*/
|
||||||
inline SharedPtr<Peer> getBestRoot() { return getBestRoot((const Address *)0,0,false); }
|
inline SharedPtr<Peer> getUpstreamPeer() { return getUpstreamPeer((const Address *)0,0,false); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the best root server, avoiding root servers listed in an array
|
* Get the current best upstream peer, avoiding those in the supplied avoid list
|
||||||
*
|
|
||||||
* This will get the best root server (lowest latency, etc.) but will
|
|
||||||
* try to avoid the listed root servers, only using them if no others
|
|
||||||
* are available.
|
|
||||||
*
|
*
|
||||||
* @param avoid Nodes to avoid
|
* @param avoid Nodes to avoid
|
||||||
* @param avoidCount Number of nodes to avoid
|
* @param avoidCount Number of nodes to avoid
|
||||||
* @param strictAvoid If false, consider avoided root servers anyway if no non-avoid root servers are available
|
* @param strictAvoid If false, consider avoided root servers anyway if no non-avoid root servers are available
|
||||||
* @return Root server or NULL if none available
|
* @return Root server or NULL if none available
|
||||||
*/
|
*/
|
||||||
SharedPtr<Peer> getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid);
|
SharedPtr<Peer> getUpstreamPeer(const Address *avoid,unsigned int avoidCount,bool strictAvoid);
|
||||||
|
|
||||||
/**
|
|
||||||
* @param id Identity to check
|
|
||||||
* @return True if this is a designated root server in this world
|
|
||||||
*/
|
|
||||||
inline bool isRoot(const Identity &id) const
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
return (std::find(_rootAddresses.begin(),_rootAddresses.end(),id.address()) != _rootAddresses.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param id Identity to check
|
* @param id Identity to check
|
||||||
@@ -145,46 +148,144 @@ public:
|
|||||||
bool isUpstream(const Identity &id) const;
|
bool isUpstream(const Identity &id) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Vector of root server addresses
|
* @param addr Address to check
|
||||||
|
* @return True if we should accept a world update from this address
|
||||||
*/
|
*/
|
||||||
inline std::vector<Address> rootAddresses() const
|
bool shouldAcceptWorldUpdateFrom(const Address &addr) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ztaddr ZeroTier address
|
||||||
|
* @return Peer role for this device
|
||||||
|
*/
|
||||||
|
ZT_PeerRole role(const Address &ztaddr) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for prohibited endpoints
|
||||||
|
*
|
||||||
|
* Right now this returns true if the designated ZT address is a root and if
|
||||||
|
* the IP (IP only, not port) does not equal any of the IPs defined in the
|
||||||
|
* current World. This is an extra little security feature in case root keys
|
||||||
|
* get appropriated or something.
|
||||||
|
*
|
||||||
|
* Otherwise it returns false.
|
||||||
|
*
|
||||||
|
* @param ztaddr ZeroTier address
|
||||||
|
* @param ipaddr IP address
|
||||||
|
* @return True if this ZT/IP pair should not be allowed to be used
|
||||||
|
*/
|
||||||
|
bool isProhibitedEndpoint(const Address &ztaddr,const InetAddress &ipaddr) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets upstreams to contact and their stable endpoints (if known)
|
||||||
|
*
|
||||||
|
* @param eps Hash table to fill with addresses and their stable endpoints
|
||||||
|
*/
|
||||||
|
inline void getUpstreamsToContact(Hashtable< Address,std::vector<InetAddress> > &eps) const
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_upstreams_m);
|
||||||
return _rootAddresses;
|
for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
|
||||||
|
std::vector<InetAddress> &ips = eps[i->identity.address()];
|
||||||
|
for(std::vector<InetAddress>::const_iterator j(i->stableEndpoints.begin());j!=i->stableEndpoints.end();++j) {
|
||||||
|
if (std::find(ips.begin(),ips.end(),*j) == ips.end())
|
||||||
|
ips.push_back(*j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
|
||||||
|
for(std::vector<World::Root>::const_iterator i(m->roots().begin());i!=m->roots().end();++i) {
|
||||||
|
std::vector<InetAddress> &ips = eps[i->identity.address()];
|
||||||
|
for(std::vector<InetAddress>::const_iterator j(i->stableEndpoints.begin());j!=i->stableEndpoints.end();++j) {
|
||||||
|
if (std::find(ips.begin(),ips.end(),*j) == ips.end())
|
||||||
|
ips.push_back(*j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(std::vector< std::pair<uint64_t,Address> >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m)
|
||||||
|
eps[m->second];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Current World (copy)
|
* @return Vector of active upstream addresses (including roots)
|
||||||
*/
|
*/
|
||||||
inline World world() const
|
inline std::vector<Address> upstreamAddresses() const
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_upstreams_m);
|
||||||
return _world;
|
return _upstreamAddresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Current world ID
|
* @return Current moons
|
||||||
*/
|
*/
|
||||||
inline uint64_t worldId() const
|
inline std::vector<World> moons() const
|
||||||
{
|
{
|
||||||
return _world.id(); // safe to read without lock, and used from within eachPeer() so don't lock
|
Mutex::Lock _l(_upstreams_m);
|
||||||
|
return _moons;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Current world timestamp
|
* @return Moon IDs we are waiting for from seeds
|
||||||
*/
|
*/
|
||||||
inline uint64_t worldTimestamp() const
|
inline std::vector<uint64_t> moonsWanted() const
|
||||||
{
|
{
|
||||||
return _world.timestamp(); // safe to read without lock, and used from within eachPeer() so don't lock
|
Mutex::Lock _l(_upstreams_m);
|
||||||
|
std::vector<uint64_t> mw;
|
||||||
|
for(std::vector< std::pair<uint64_t,Address> >::const_iterator s(_moonSeeds.begin());s!=_moonSeeds.end();++s) {
|
||||||
|
if (std::find(mw.begin(),mw.end(),s->first) == mw.end())
|
||||||
|
mw.push_back(s->first);
|
||||||
|
}
|
||||||
|
return mw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Current planet
|
||||||
|
*/
|
||||||
|
inline World planet() const
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_upstreams_m);
|
||||||
|
return _planet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Current planet's world ID
|
||||||
|
*/
|
||||||
|
inline uint64_t planetWorldId() const
|
||||||
|
{
|
||||||
|
return _planet.id(); // safe to read without lock, and used from within eachPeer() so don't lock
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Current planet's world timestamp
|
||||||
|
*/
|
||||||
|
inline uint64_t planetWorldTimestamp() const
|
||||||
|
{
|
||||||
|
return _planet.timestamp(); // safe to read without lock, and used from within eachPeer() so don't lock
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate new world and update if newer and signature is okay
|
* Validate new world and update if newer and signature is okay
|
||||||
*
|
*
|
||||||
* @param newWorld Potential new world definition revision
|
* @param newWorld A new or updated planet or moon to learn
|
||||||
* @return True if an update actually occurred
|
* @param alwaysAcceptNew If true, always accept new moons even if we're not waiting for one
|
||||||
|
* @return True if it was valid and newer than current (or totally new for moons)
|
||||||
*/
|
*/
|
||||||
bool worldUpdateIfValid(const World &newWorld);
|
bool addWorld(const World &newWorld,bool alwaysAcceptNew);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a moon
|
||||||
|
*
|
||||||
|
* This loads it from moons.d if present, and if not adds it to
|
||||||
|
* a list of moons that we want to contact.
|
||||||
|
*
|
||||||
|
* @param id Moon ID
|
||||||
|
* @param seed If non-NULL, an address of any member of the moon to contact
|
||||||
|
*/
|
||||||
|
void addMoon(const uint64_t id,const Address &seed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a moon
|
||||||
|
*
|
||||||
|
* @param id Moon's world ID
|
||||||
|
*/
|
||||||
|
void removeMoon(const uint64_t id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean and flush database
|
* Clean and flush database
|
||||||
@@ -198,7 +299,7 @@ public:
|
|||||||
inline unsigned long countActive(uint64_t now) const
|
inline unsigned long countActive(uint64_t now) const
|
||||||
{
|
{
|
||||||
unsigned long cnt = 0;
|
unsigned long cnt = 0;
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_peers_m);
|
||||||
Hashtable< Address,SharedPtr<Peer> >::Iterator i(const_cast<Topology *>(this)->_peers);
|
Hashtable< Address,SharedPtr<Peer> >::Iterator i(const_cast<Topology *>(this)->_peers);
|
||||||
Address *a = (Address *)0;
|
Address *a = (Address *)0;
|
||||||
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
||||||
@@ -211,20 +312,13 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Apply a function or function object to all peers
|
* Apply a function or function object to all peers
|
||||||
*
|
*
|
||||||
* Note: explicitly template this by reference if you want the object
|
|
||||||
* passed by reference instead of copied.
|
|
||||||
*
|
|
||||||
* Warning: be careful not to use features in these that call any other
|
|
||||||
* methods of Topology that may lock _lock, otherwise a recursive lock
|
|
||||||
* and deadlock or lock corruption may occur.
|
|
||||||
*
|
|
||||||
* @param f Function to apply
|
* @param f Function to apply
|
||||||
* @tparam F Function or function object type
|
* @tparam F Function or function object type
|
||||||
*/
|
*/
|
||||||
template<typename F>
|
template<typename F>
|
||||||
inline void eachPeer(F f)
|
inline void eachPeer(F f)
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_peers_m);
|
||||||
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
|
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
|
||||||
Address *a = (Address *)0;
|
Address *a = (Address *)0;
|
||||||
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
||||||
@@ -244,14 +338,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline std::vector< std::pair< Address,SharedPtr<Peer> > > allPeers() const
|
inline std::vector< std::pair< Address,SharedPtr<Peer> > > allPeers() const
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_peers_m);
|
||||||
return _peers.entries();
|
return _peers.entries();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if I am a root server in the current World
|
* @return True if I am a root server in a planet or moon
|
||||||
*/
|
*/
|
||||||
inline bool amRoot() const throw() { return _amRoot; }
|
inline bool amRoot() const { return _amRoot; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the outbound trusted path ID for a physical address, or 0 if none
|
* Get the outbound trusted path ID for a physical address, or 0 if none
|
||||||
@@ -294,7 +388,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (count > ZT_MAX_TRUSTED_PATHS)
|
if (count > ZT_MAX_TRUSTED_PATHS)
|
||||||
count = ZT_MAX_TRUSTED_PATHS;
|
count = ZT_MAX_TRUSTED_PATHS;
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_trustedPaths_m);
|
||||||
for(unsigned int i=0;i<count;++i) {
|
for(unsigned int i=0;i<count;++i) {
|
||||||
_trustedPathIds[i] = ids[i];
|
_trustedPathIds[i] = ids[i];
|
||||||
_trustedPathNetworks[i] = networks[i];
|
_trustedPathNetworks[i] = networks[i];
|
||||||
@@ -302,22 +396,49 @@ public:
|
|||||||
_trustedPathCount = count;
|
_trustedPathCount = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Current certificate of representation (copy)
|
||||||
|
*/
|
||||||
|
inline CertificateOfRepresentation certificateOfRepresentation() const
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_upstreams_m);
|
||||||
|
return _cor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param buf Buffer to receive COR
|
||||||
|
*/
|
||||||
|
template<unsigned int C>
|
||||||
|
void appendCertificateOfRepresentation(Buffer<C> &buf)
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_upstreams_m);
|
||||||
|
_cor.serialize(buf);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Identity _getIdentity(const Address &zta);
|
Identity _getIdentity(const Address &zta);
|
||||||
void _setWorld(const World &newWorld);
|
void _memoizeUpstreams();
|
||||||
|
|
||||||
const RuntimeEnvironment *const RR;
|
const RuntimeEnvironment *const RR;
|
||||||
|
|
||||||
uint64_t _trustedPathIds[ZT_MAX_TRUSTED_PATHS];
|
uint64_t _trustedPathIds[ZT_MAX_TRUSTED_PATHS];
|
||||||
InetAddress _trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
|
InetAddress _trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
|
||||||
unsigned int _trustedPathCount;
|
unsigned int _trustedPathCount;
|
||||||
World _world;
|
Mutex _trustedPaths_m;
|
||||||
Hashtable< Address,SharedPtr<Peer> > _peers;
|
|
||||||
std::vector< Address > _rootAddresses;
|
|
||||||
std::vector< SharedPtr<Peer> > _rootPeers;
|
|
||||||
bool _amRoot;
|
|
||||||
|
|
||||||
Mutex _lock;
|
Hashtable< Address,SharedPtr<Peer> > _peers;
|
||||||
|
Mutex _peers_m;
|
||||||
|
|
||||||
|
Hashtable< Path::HashKey,SharedPtr<Path> > _paths;
|
||||||
|
Mutex _paths_m;
|
||||||
|
|
||||||
|
World _planet;
|
||||||
|
std::vector<World> _moons;
|
||||||
|
std::vector< std::pair<uint64_t,Address> > _moonSeeds;
|
||||||
|
std::vector<Address> _upstreamAddresses;
|
||||||
|
CertificateOfRepresentation _cor;
|
||||||
|
bool _amRoot;
|
||||||
|
Mutex _upstreams_m; // locks worlds, upstream info, moon info, etc.
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -47,21 +47,14 @@ namespace ZeroTier {
|
|||||||
|
|
||||||
const char Utils::HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
|
const char Utils::HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
|
||||||
|
|
||||||
static void _Utils_doBurn(char *ptr,unsigned int len)
|
// Crazy hack to force memory to be securely zeroed in spite of the best efforts of optimizing compilers.
|
||||||
|
static void _Utils_doBurn(volatile uint8_t *ptr,unsigned int len)
|
||||||
{
|
{
|
||||||
for(unsigned int i=0;i<len;++i)
|
volatile uint8_t *const end = ptr + len;
|
||||||
ptr[i] = (char)0;
|
while (ptr != end) *(ptr++) = (uint8_t)0;
|
||||||
}
|
|
||||||
void (*volatile _Utils_doBurn_ptr)(char *,unsigned int) = _Utils_doBurn;
|
|
||||||
void Utils::burn(void *ptr,unsigned int len)
|
|
||||||
throw()
|
|
||||||
{
|
|
||||||
// Ridiculous hack: call _doBurn() via a volatile function pointer to
|
|
||||||
// hold down compiler optimizers and beat them mercilessly until they
|
|
||||||
// cry and mumble something about never eliding secure memory zeroing
|
|
||||||
// again.
|
|
||||||
(_Utils_doBurn_ptr)((char *)ptr,len);
|
|
||||||
}
|
}
|
||||||
|
static void (*volatile _Utils_doBurn_ptr)(volatile uint8_t *,unsigned int) = _Utils_doBurn;
|
||||||
|
void Utils::burn(void *ptr,unsigned int len) { (_Utils_doBurn_ptr)((volatile uint8_t *)ptr,len); }
|
||||||
|
|
||||||
std::string Utils::hex(const void *data,unsigned int len)
|
std::string Utils::hex(const void *data,unsigned int len)
|
||||||
{
|
{
|
||||||
@@ -144,6 +137,8 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
|
|||||||
static Mutex globalLock;
|
static Mutex globalLock;
|
||||||
static Salsa20 s20;
|
static Salsa20 s20;
|
||||||
static bool s20Initialized = false;
|
static bool s20Initialized = false;
|
||||||
|
static uint8_t randomBuf[65536];
|
||||||
|
static unsigned int randomPtr = sizeof(randomBuf);
|
||||||
|
|
||||||
Mutex::Lock _l(globalLock);
|
Mutex::Lock _l(globalLock);
|
||||||
|
|
||||||
@@ -168,27 +163,31 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
|
|||||||
|
|
||||||
static HCRYPTPROV cryptProvider = NULL;
|
static HCRYPTPROV cryptProvider = NULL;
|
||||||
|
|
||||||
|
for(unsigned int i=0;i<bytes;++i) {
|
||||||
|
if (randomPtr >= sizeof(randomBuf)) {
|
||||||
if (cryptProvider == NULL) {
|
if (cryptProvider == NULL) {
|
||||||
if (!CryptAcquireContextA(&cryptProvider,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
|
if (!CryptAcquireContextA(&cryptProvider,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
|
||||||
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to obtain WinCrypt context!\r\n");
|
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to obtain WinCrypt context!\r\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!CryptGenRandom(cryptProvider,(DWORD)bytes,(BYTE *)buf)) {
|
if (!CryptGenRandom(cryptProvider,(DWORD)sizeof(randomBuf),(BYTE *)randomBuf)) {
|
||||||
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
|
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
randomPtr = 0;
|
||||||
|
s20.crypt12(randomBuf,randomBuf,sizeof(randomBuf));
|
||||||
|
}
|
||||||
|
((uint8_t *)buf)[i] = randomBuf[randomPtr++];
|
||||||
|
}
|
||||||
|
|
||||||
#else // not __WINDOWS__
|
#else // not __WINDOWS__
|
||||||
|
|
||||||
static char randomBuf[131072];
|
|
||||||
static unsigned int randomPtr = sizeof(randomBuf);
|
|
||||||
static int devURandomFd = -1;
|
static int devURandomFd = -1;
|
||||||
|
|
||||||
if (devURandomFd <= 0) {
|
if (devURandomFd < 0) {
|
||||||
devURandomFd = ::open("/dev/urandom",O_RDONLY);
|
devURandomFd = ::open("/dev/urandom",O_RDONLY);
|
||||||
if (devURandomFd <= 0) {
|
if (devURandomFd < 0) {
|
||||||
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
|
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
return;
|
return;
|
||||||
@@ -201,7 +200,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
|
|||||||
if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) {
|
if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) {
|
||||||
::close(devURandomFd);
|
::close(devURandomFd);
|
||||||
devURandomFd = ::open("/dev/urandom",O_RDONLY);
|
devURandomFd = ::open("/dev/urandom",O_RDONLY);
|
||||||
if (devURandomFd <= 0) {
|
if (devURandomFd < 0) {
|
||||||
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
|
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
return;
|
return;
|
||||||
@@ -209,57 +208,12 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
|
|||||||
} else break;
|
} else break;
|
||||||
}
|
}
|
||||||
randomPtr = 0;
|
randomPtr = 0;
|
||||||
|
s20.crypt12(randomBuf,randomBuf,sizeof(randomBuf));
|
||||||
}
|
}
|
||||||
((char *)buf)[i] = randomBuf[randomPtr++];
|
((uint8_t *)buf)[i] = randomBuf[randomPtr++];
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __WINDOWS__ or not
|
#endif // __WINDOWS__ or not
|
||||||
|
|
||||||
s20.encrypt12(buf,buf,bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> Utils::split(const char *s,const char *const sep,const char *esc,const char *quot)
|
|
||||||
{
|
|
||||||
std::vector<std::string> fields;
|
|
||||||
std::string buf;
|
|
||||||
|
|
||||||
if (!esc)
|
|
||||||
esc = "";
|
|
||||||
if (!quot)
|
|
||||||
quot = "";
|
|
||||||
|
|
||||||
bool escapeState = false;
|
|
||||||
char quoteState = 0;
|
|
||||||
while (*s) {
|
|
||||||
if (escapeState) {
|
|
||||||
escapeState = false;
|
|
||||||
buf.push_back(*s);
|
|
||||||
} else if (quoteState) {
|
|
||||||
if (*s == quoteState) {
|
|
||||||
quoteState = 0;
|
|
||||||
fields.push_back(buf);
|
|
||||||
buf.clear();
|
|
||||||
} else buf.push_back(*s);
|
|
||||||
} else {
|
|
||||||
const char *quotTmp;
|
|
||||||
if (strchr(esc,*s))
|
|
||||||
escapeState = true;
|
|
||||||
else if ((buf.size() <= 0)&&((quotTmp = strchr(quot,*s))))
|
|
||||||
quoteState = *quotTmp;
|
|
||||||
else if (strchr(sep,*s)) {
|
|
||||||
if (buf.size() > 0) {
|
|
||||||
fields.push_back(buf);
|
|
||||||
buf.clear();
|
|
||||||
} // else skip runs of seperators
|
|
||||||
} else buf.push_back(*s);
|
|
||||||
}
|
|
||||||
++s;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf.size())
|
|
||||||
fields.push_back(buf);
|
|
||||||
|
|
||||||
return fields;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::scopy(char *dest,unsigned int len,const char *src)
|
bool Utils::scopy(char *dest,unsigned int len,const char *src)
|
||||||
|
|||||||
@@ -59,8 +59,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Securely zero memory, avoiding compiler optimizations and such
|
* Securely zero memory, avoiding compiler optimizations and such
|
||||||
*/
|
*/
|
||||||
static void burn(void *ptr,unsigned int len)
|
static void burn(void *ptr,unsigned int len);
|
||||||
throw();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert binary data to hexadecimal
|
* Convert binary data to hexadecimal
|
||||||
@@ -111,17 +110,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
static void getSecureRandom(void *buf,unsigned int bytes);
|
static void getSecureRandom(void *buf,unsigned int bytes);
|
||||||
|
|
||||||
/**
|
|
||||||
* Split a string by delimiter, with optional escape and quote characters
|
|
||||||
*
|
|
||||||
* @param s String to split
|
|
||||||
* @param sep One or more separators
|
|
||||||
* @param esc Zero or more escape characters
|
|
||||||
* @param quot Zero or more quote characters
|
|
||||||
* @return Vector of tokens
|
|
||||||
*/
|
|
||||||
static std::vector<std::string> split(const char *s,const char *const sep,const char *esc,const char *quot);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tokenize a string (alias for strtok_r or strtok_s depending on platform)
|
* Tokenize a string (alias for strtok_r or strtok_s depending on platform)
|
||||||
*
|
*
|
||||||
@@ -264,6 +252,20 @@ public:
|
|||||||
return ((((v + (v >> 4)) & (uint32_t)0xF0F0F0F) * (uint32_t)0x1010101) >> 24);
|
return ((((v + (v >> 4)) & (uint32_t)0xF0F0F0F) * (uint32_t)0x1010101) >> 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count the number of bits set in an integer
|
||||||
|
*
|
||||||
|
* @param v 64-bit integer
|
||||||
|
* @return Number of bits set in this integer (0-64)
|
||||||
|
*/
|
||||||
|
static inline uint64_t countBits(uint64_t v)
|
||||||
|
{
|
||||||
|
v = v - ((v >> 1) & (uint64_t)~(uint64_t)0/3);
|
||||||
|
v = (v & (uint64_t)~(uint64_t)0/15*3) + ((v >> 2) & (uint64_t)~(uint64_t)0/15*3);
|
||||||
|
v = (v + (v >> 4)) & (uint64_t)~(uint64_t)0/255*15;
|
||||||
|
return (uint64_t)(v * ((uint64_t)~(uint64_t)0/255)) >> 56;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a memory buffer is all-zero
|
* Check if a memory buffer is all-zero
|
||||||
*
|
*
|
||||||
@@ -346,8 +348,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @return -1, 0, or 1 based on whether first tuple is less than, equal to, or greater than second
|
* @return -1, 0, or 1 based on whether first tuple is less than, equal to, or greater than second
|
||||||
*/
|
*/
|
||||||
static inline int compareVersion(unsigned int maj1,unsigned int min1,unsigned int rev1,unsigned int maj2,unsigned int min2,unsigned int rev2)
|
static inline int compareVersion(unsigned int maj1,unsigned int min1,unsigned int rev1,unsigned int b1,unsigned int maj2,unsigned int min2,unsigned int rev2,unsigned int b2)
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
if (maj1 > maj2)
|
if (maj1 > maj2)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -363,10 +364,16 @@ public:
|
|||||||
return 1;
|
return 1;
|
||||||
else if (rev1 < rev2)
|
else if (rev1 < rev2)
|
||||||
return -1;
|
return -1;
|
||||||
|
else {
|
||||||
|
if (b1 > b2)
|
||||||
|
return 1;
|
||||||
|
else if (b1 < b2)
|
||||||
|
return -1;
|
||||||
else return 0;
|
else return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hexadecimal characters 0-f
|
* Hexadecimal characters 0-f
|
||||||
|
|||||||
@@ -48,16 +48,6 @@
|
|||||||
*/
|
*/
|
||||||
#define ZT_WORLD_MAX_SERIALIZED_LENGTH (((1024 + (32 * ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)) * ZT_WORLD_MAX_ROOTS) + ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_SIGNATURE_LEN + 128)
|
#define ZT_WORLD_MAX_SERIALIZED_LENGTH (((1024 + (32 * ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)) * ZT_WORLD_MAX_ROOTS) + ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_SIGNATURE_LEN + 128)
|
||||||
|
|
||||||
/**
|
|
||||||
* World ID indicating null / empty World object
|
|
||||||
*/
|
|
||||||
#define ZT_WORLD_ID_NULL 0
|
|
||||||
|
|
||||||
/**
|
|
||||||
* World ID for a test network with ephemeral or temporary roots
|
|
||||||
*/
|
|
||||||
#define ZT_WORLD_ID_TESTNET 1
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* World ID for Earth
|
* World ID for Earth
|
||||||
*
|
*
|
||||||
@@ -90,15 +80,23 @@ namespace ZeroTier {
|
|||||||
* orbits, the Moon (about 1.3 light seconds), and nearby Lagrange points. A
|
* orbits, the Moon (about 1.3 light seconds), and nearby Lagrange points. A
|
||||||
* world ID for Mars and nearby space is defined but not yet used, and a test
|
* world ID for Mars and nearby space is defined but not yet used, and a test
|
||||||
* world ID is provided for testing purposes.
|
* world ID is provided for testing purposes.
|
||||||
*
|
|
||||||
* If you absolutely must run your own "unofficial" ZeroTier network, please
|
|
||||||
* define your world IDs above 0xffffffff (4294967295). Code to make a World
|
|
||||||
* is in mkworld.cpp in the parent directory and must be edited to change
|
|
||||||
* settings.
|
|
||||||
*/
|
*/
|
||||||
class World
|
class World
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* World type -- do not change IDs
|
||||||
|
*/
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
TYPE_NULL = 0,
|
||||||
|
TYPE_PLANET = 1, // Planets, of which there is currently one (Earth)
|
||||||
|
TYPE_MOON = 127 // Moons, which are user-created and many
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upstream server definition in world/moon
|
||||||
|
*/
|
||||||
struct Root
|
struct Root
|
||||||
{
|
{
|
||||||
Identity identity;
|
Identity identity;
|
||||||
@@ -113,45 +111,54 @@ public:
|
|||||||
* Construct an empty / null World
|
* Construct an empty / null World
|
||||||
*/
|
*/
|
||||||
World() :
|
World() :
|
||||||
_id(ZT_WORLD_ID_NULL),
|
_id(0),
|
||||||
_ts(0) {}
|
_ts(0),
|
||||||
|
_type(TYPE_NULL) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Root servers for this world and their stable endpoints
|
* @return Root servers for this world and their stable endpoints
|
||||||
*/
|
*/
|
||||||
inline const std::vector<World::Root> &roots() const throw() { return _roots; }
|
inline const std::vector<World::Root> &roots() const { return _roots; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return World type: planet or moon
|
||||||
|
*/
|
||||||
|
inline Type type() const { return _type; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return World unique identifier
|
* @return World unique identifier
|
||||||
*/
|
*/
|
||||||
inline uint64_t id() const throw() { return _id; }
|
inline uint64_t id() const { return _id; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return World definition timestamp
|
* @return World definition timestamp
|
||||||
*/
|
*/
|
||||||
inline uint64_t timestamp() const throw() { return _ts; }
|
inline uint64_t timestamp() const { return _ts; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return C25519 signature
|
||||||
|
*/
|
||||||
|
inline const C25519::Signature &signature() const { return _signature; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Public key that must sign next update
|
||||||
|
*/
|
||||||
|
inline const C25519::Public &updatesMustBeSignedBy() const { return _updatesMustBeSignedBy; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a world update should replace this one
|
* Check whether a world update should replace this one
|
||||||
*
|
*
|
||||||
* A new world update is valid if it is for the same world ID, is newer,
|
|
||||||
* and is signed by the current world's signing key. If this world object
|
|
||||||
* is null, it can always be updated.
|
|
||||||
*
|
|
||||||
* @param update Candidate update
|
* @param update Candidate update
|
||||||
* @param fullSignatureCheck Perform full cryptographic signature check (true == yes, false == skip)
|
* @return True if update is newer than current, matches its ID and type, and is properly signed (or if current is NULL)
|
||||||
* @return True if update is newer than current and is properly signed
|
|
||||||
*/
|
*/
|
||||||
inline bool shouldBeReplacedBy(const World &update,bool fullSignatureCheck)
|
inline bool shouldBeReplacedBy(const World &update)
|
||||||
{
|
{
|
||||||
if (_id == ZT_WORLD_ID_NULL)
|
if ((_id == 0)||(_type == TYPE_NULL))
|
||||||
return true;
|
return true;
|
||||||
if ((_id == update._id)&&(_ts < update._ts)) {
|
if ((_id == update._id)&&(_ts < update._ts)&&(_type == update._type)) {
|
||||||
if (fullSignatureCheck) {
|
|
||||||
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
|
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
|
||||||
update.serialize(tmp,true);
|
update.serialize(tmp,true);
|
||||||
return C25519::verify(_updateSigningKey,tmp.data(),tmp.size(),update._signature);
|
return C25519::verify(_updatesMustBeSignedBy,tmp.data(),tmp.size(),update._signature);
|
||||||
} else return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -159,17 +166,17 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @return True if this World is non-empty
|
* @return True if this World is non-empty
|
||||||
*/
|
*/
|
||||||
inline operator bool() const throw() { return (_id != ZT_WORLD_ID_NULL); }
|
inline operator bool() const { return (_type != TYPE_NULL); }
|
||||||
|
|
||||||
template<unsigned int C>
|
template<unsigned int C>
|
||||||
inline void serialize(Buffer<C> &b,bool forSign = false) const
|
inline void serialize(Buffer<C> &b,bool forSign = false) const
|
||||||
{
|
{
|
||||||
if (forSign)
|
if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||||
b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
|
||||||
b.append((uint8_t)0x01); // version -- only one valid value for now
|
b.append((uint8_t)_type);
|
||||||
b.append((uint64_t)_id);
|
b.append((uint64_t)_id);
|
||||||
b.append((uint64_t)_ts);
|
b.append((uint64_t)_ts);
|
||||||
b.append(_updateSigningKey.data,ZT_C25519_PUBLIC_KEY_LEN);
|
b.append(_updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN);
|
||||||
if (!forSign)
|
if (!forSign)
|
||||||
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
|
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
|
||||||
b.append((uint8_t)_roots.size());
|
b.append((uint8_t)_roots.size());
|
||||||
@@ -179,8 +186,10 @@ public:
|
|||||||
for(std::vector<InetAddress>::const_iterator ep(r->stableEndpoints.begin());ep!=r->stableEndpoints.end();++ep)
|
for(std::vector<InetAddress>::const_iterator ep(r->stableEndpoints.begin());ep!=r->stableEndpoints.end();++ep)
|
||||||
ep->serialize(b);
|
ep->serialize(b);
|
||||||
}
|
}
|
||||||
if (forSign)
|
if (_type == TYPE_MOON)
|
||||||
b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
|
b.append((uint16_t)0); // no attached dictionary (for future use)
|
||||||
|
|
||||||
|
if (forSign) b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned int C>
|
template<unsigned int C>
|
||||||
@@ -190,14 +199,19 @@ public:
|
|||||||
|
|
||||||
_roots.clear();
|
_roots.clear();
|
||||||
|
|
||||||
if (b[p++] != 0x01)
|
switch((Type)b[p++]) {
|
||||||
throw std::invalid_argument("invalid World serialized version");
|
case TYPE_NULL: _type = TYPE_NULL; break; // shouldn't ever really happen in serialized data but it's not invalid
|
||||||
|
case TYPE_PLANET: _type = TYPE_PLANET; break;
|
||||||
|
case TYPE_MOON: _type = TYPE_MOON; break;
|
||||||
|
default:
|
||||||
|
throw std::invalid_argument("invalid world type");
|
||||||
|
}
|
||||||
|
|
||||||
_id = b.template at<uint64_t>(p); p += 8;
|
_id = b.template at<uint64_t>(p); p += 8;
|
||||||
_ts = b.template at<uint64_t>(p); p += 8;
|
_ts = b.template at<uint64_t>(p); p += 8;
|
||||||
memcpy(_updateSigningKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
|
memcpy(_updatesMustBeSignedBy.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
|
||||||
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
|
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
|
||||||
unsigned int numRoots = b[p++];
|
const unsigned int numRoots = (unsigned int)b[p++];
|
||||||
if (numRoots > ZT_WORLD_MAX_ROOTS)
|
if (numRoots > ZT_WORLD_MAX_ROOTS)
|
||||||
throw std::invalid_argument("too many roots in World");
|
throw std::invalid_argument("too many roots in World");
|
||||||
for(unsigned int k=0;k<numRoots;++k) {
|
for(unsigned int k=0;k<numRoots;++k) {
|
||||||
@@ -212,17 +226,49 @@ public:
|
|||||||
p += r.stableEndpoints.back().deserialize(b,p);
|
p += r.stableEndpoints.back().deserialize(b,p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (_type == TYPE_MOON)
|
||||||
|
p += b.template at<uint16_t>(p) + 2;
|
||||||
|
|
||||||
return (p - startAt);
|
return (p - startAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(const World &w) const throw() { return ((_id == w._id)&&(_ts == w._ts)&&(_updateSigningKey == w._updateSigningKey)&&(_signature == w._signature)&&(_roots == w._roots)); }
|
inline bool operator==(const World &w) const { return ((_id == w._id)&&(_ts == w._ts)&&(_updatesMustBeSignedBy == w._updatesMustBeSignedBy)&&(_signature == w._signature)&&(_roots == w._roots)&&(_type == w._type)); }
|
||||||
inline bool operator!=(const World &w) const throw() { return (!(*this == w)); }
|
inline bool operator!=(const World &w) const { return (!(*this == w)); }
|
||||||
|
|
||||||
|
inline bool operator<(const World &w) const { return (((int)_type < (int)w._type) ? true : ((_type == w._type) ? (_id < w._id) : false)); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a World object signed with a key pair
|
||||||
|
*
|
||||||
|
* @param t World type
|
||||||
|
* @param id World ID
|
||||||
|
* @param ts World timestamp / revision
|
||||||
|
* @param sk Key that must be used to sign the next future update to this world
|
||||||
|
* @param roots Roots and their stable endpoints
|
||||||
|
* @param signWith Key to sign this World with (can have the same public as the next-update signing key, but doesn't have to)
|
||||||
|
* @return Signed World object
|
||||||
|
*/
|
||||||
|
static inline World make(World::Type t,uint64_t id,uint64_t ts,const C25519::Public &sk,const std::vector<World::Root> &roots,const C25519::Pair &signWith)
|
||||||
|
{
|
||||||
|
World w;
|
||||||
|
w._id = id;
|
||||||
|
w._ts = ts;
|
||||||
|
w._type = t;
|
||||||
|
w._updatesMustBeSignedBy = sk;
|
||||||
|
w._roots = roots;
|
||||||
|
|
||||||
|
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
|
||||||
|
w.serialize(tmp,true);
|
||||||
|
w._signature = C25519::sign(signWith,tmp.data(),tmp.size());
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint64_t _id;
|
uint64_t _id;
|
||||||
uint64_t _ts;
|
uint64_t _ts;
|
||||||
C25519::Public _updateSigningKey;
|
Type _type;
|
||||||
|
C25519::Public _updatesMustBeSignedBy;
|
||||||
C25519::Signature _signature;
|
C25519::Signature _signature;
|
||||||
std::vector<Root> _roots;
|
std::vector<Root> _roots;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
OBJS=\
|
OBJS=\
|
||||||
|
controller/EmbeddedNetworkController.o \
|
||||||
|
controller/JSONDB.o \
|
||||||
node/C25519.o \
|
node/C25519.o \
|
||||||
|
node/Capability.o \
|
||||||
node/CertificateOfMembership.o \
|
node/CertificateOfMembership.o \
|
||||||
|
node/CertificateOfOwnership.o \
|
||||||
node/Cluster.o \
|
node/Cluster.o \
|
||||||
node/DeferredPackets.o \
|
|
||||||
node/Identity.o \
|
node/Identity.o \
|
||||||
node/IncomingPacket.o \
|
node/IncomingPacket.o \
|
||||||
node/InetAddress.o \
|
node/InetAddress.o \
|
||||||
|
node/Membership.o \
|
||||||
node/Multicaster.o \
|
node/Multicaster.o \
|
||||||
node/Network.o \
|
node/Network.o \
|
||||||
node/NetworkConfig.o \
|
node/NetworkConfig.o \
|
||||||
@@ -15,15 +19,17 @@ OBJS=\
|
|||||||
node/Path.o \
|
node/Path.o \
|
||||||
node/Peer.o \
|
node/Peer.o \
|
||||||
node/Poly1305.o \
|
node/Poly1305.o \
|
||||||
|
node/Revocation.o \
|
||||||
node/Salsa20.o \
|
node/Salsa20.o \
|
||||||
node/SelfAwareness.o \
|
node/SelfAwareness.o \
|
||||||
node/SHA512.o \
|
node/SHA512.o \
|
||||||
node/Switch.o \
|
node/Switch.o \
|
||||||
|
node/Tag.o \
|
||||||
node/Topology.o \
|
node/Topology.o \
|
||||||
node/Utils.o \
|
node/Utils.o \
|
||||||
osdep/BackgroundResolver.o \
|
|
||||||
osdep/ManagedRoute.o \
|
osdep/ManagedRoute.o \
|
||||||
osdep/Http.o \
|
osdep/Http.o \
|
||||||
osdep/OSUtils.o \
|
osdep/OSUtils.o \
|
||||||
service/ClusterGeoIpService.o \
|
service/ClusterGeoIpService.o \
|
||||||
service/ControlPlane.o
|
service/ControlPlane.o \
|
||||||
|
service/SoftwareUpdater.o
|
||||||
|
|||||||
@@ -43,31 +43,40 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#ifdef __LINUX__
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/securebits.h>
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "include/ZeroTierOne.h"
|
#include "include/ZeroTierOne.h"
|
||||||
|
|
||||||
#ifdef ZT_USE_SYSTEM_JSON_PARSER
|
|
||||||
#include <json-parser/json.h>
|
|
||||||
#else
|
|
||||||
#include "ext/json-parser/json.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "node/Identity.hpp"
|
#include "node/Identity.hpp"
|
||||||
#include "node/CertificateOfMembership.hpp"
|
#include "node/CertificateOfMembership.hpp"
|
||||||
#include "node/Utils.hpp"
|
#include "node/Utils.hpp"
|
||||||
#include "node/NetworkController.hpp"
|
#include "node/NetworkController.hpp"
|
||||||
|
#include "node/Buffer.hpp"
|
||||||
|
#include "node/World.hpp"
|
||||||
|
|
||||||
#include "osdep/OSUtils.hpp"
|
#include "osdep/OSUtils.hpp"
|
||||||
#include "osdep/Http.hpp"
|
#include "osdep/Http.hpp"
|
||||||
|
|
||||||
#include "service/OneService.hpp"
|
#include "service/OneService.hpp"
|
||||||
|
|
||||||
|
#include "ext/json/json.hpp"
|
||||||
|
|
||||||
#define ZT_PID_PATH "zerotier-one.pid"
|
#define ZT_PID_PATH "zerotier-one.pid"
|
||||||
|
|
||||||
using namespace ZeroTier;
|
using namespace ZeroTier;
|
||||||
@@ -112,6 +121,9 @@ static void cliPrintHelp(const char *pn,FILE *out)
|
|||||||
fprintf(out," join <network> - Join a network" ZT_EOL_S);
|
fprintf(out," join <network> - Join a network" ZT_EOL_S);
|
||||||
fprintf(out," leave <network> - Leave a network" ZT_EOL_S);
|
fprintf(out," leave <network> - Leave a network" ZT_EOL_S);
|
||||||
fprintf(out," set <network> <setting> - Set a network setting" ZT_EOL_S);
|
fprintf(out," set <network> <setting> - Set a network setting" ZT_EOL_S);
|
||||||
|
fprintf(out," listmoons - List moons (federated root sets)" ZT_EOL_S);
|
||||||
|
fprintf(out," orbit <world ID> <seed> - Join a moon via any member root" ZT_EOL_S);
|
||||||
|
fprintf(out," deorbit <world ID> - Leave a moon" ZT_EOL_S);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string cliFixJsonCRs(const std::string &s)
|
static std::string cliFixJsonCRs(const std::string &s)
|
||||||
@@ -283,221 +295,146 @@ static int cli(int argc,char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else if ((command == "info")||(command == "status")) {
|
} else if ((command == "info")||(command == "status")) {
|
||||||
unsigned int scode = Http::GET(
|
const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/status",requestHeaders,responseHeaders,responseBody);
|
||||||
1024 * 1024 * 16,
|
|
||||||
60000,
|
nlohmann::json j;
|
||||||
(const struct sockaddr *)&addr,
|
try {
|
||||||
"/status",
|
j = OSUtils::jsonParse(responseBody);
|
||||||
requestHeaders,
|
} catch (std::exception &exc) {
|
||||||
responseHeaders,
|
printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
|
||||||
responseBody);
|
return 1;
|
||||||
if (scode == 200) {
|
} catch ( ... ) {
|
||||||
if (json) {
|
printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
|
||||||
printf("%s",cliFixJsonCRs(responseBody).c_str());
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
json_value *j = json_parse(responseBody.c_str(),responseBody.length());
|
|
||||||
bool good = false;
|
|
||||||
if (j) {
|
|
||||||
if (j->type == json_object) {
|
|
||||||
const char *address = (const char *)0;
|
|
||||||
bool online = false;
|
|
||||||
const char *version = (const char *)0;
|
|
||||||
for(unsigned int k=0;k<j->u.object.length;++k) {
|
|
||||||
if ((!strcmp(j->u.object.values[k].name,"address"))&&(j->u.object.values[k].value->type == json_string))
|
|
||||||
address = j->u.object.values[k].value->u.string.ptr;
|
|
||||||
else if ((!strcmp(j->u.object.values[k].name,"version"))&&(j->u.object.values[k].value->type == json_string))
|
|
||||||
version = j->u.object.values[k].value->u.string.ptr;
|
|
||||||
else if ((!strcmp(j->u.object.values[k].name,"online"))&&(j->u.object.values[k].value->type == json_boolean))
|
|
||||||
online = (j->u.object.values[k].value->u.boolean != 0);
|
|
||||||
}
|
|
||||||
if ((address)&&(version)) {
|
|
||||||
printf("200 info %s %s %s" ZT_EOL_S,address,(online ? "ONLINE" : "OFFLINE"),version);
|
|
||||||
good = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
json_value_free(j);
|
|
||||||
}
|
|
||||||
if (good) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
printf("%u %s invalid JSON response" ZT_EOL_S,scode,command.c_str());
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (scode == 200) {
|
||||||
|
if (json) {
|
||||||
|
printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
|
||||||
|
} else {
|
||||||
|
if (j.is_object()) {
|
||||||
|
printf("200 info %s %s %s" ZT_EOL_S,
|
||||||
|
OSUtils::jsonString(j["address"],"-").c_str(),
|
||||||
|
OSUtils::jsonString(j["version"],"-").c_str(),
|
||||||
|
((j["tcpFallbackActive"]) ? "TUNNELED" : ((j["online"]) ? "ONLINE" : "OFFLINE")));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
|
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else if (command == "listpeers") {
|
} else if (command == "listpeers") {
|
||||||
unsigned int scode = Http::GET(
|
const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/peer",requestHeaders,responseHeaders,responseBody);
|
||||||
1024 * 1024 * 16,
|
|
||||||
60000,
|
nlohmann::json j;
|
||||||
(const struct sockaddr *)&addr,
|
try {
|
||||||
"/peer",
|
j = OSUtils::jsonParse(responseBody);
|
||||||
requestHeaders,
|
} catch (std::exception &exc) {
|
||||||
responseHeaders,
|
printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
|
||||||
responseBody);
|
return 1;
|
||||||
|
} catch ( ... ) {
|
||||||
|
printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (scode == 200) {
|
if (scode == 200) {
|
||||||
if (json) {
|
if (json) {
|
||||||
printf("%s",cliFixJsonCRs(responseBody).c_str());
|
printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
|
||||||
return 0;
|
|
||||||
} else {
|
} else {
|
||||||
printf("200 listpeers <ztaddr> <paths> <latency> <version> <role>" ZT_EOL_S);
|
printf("200 listpeers <ztaddr> <path> <latency> <version> <role>" ZT_EOL_S);
|
||||||
json_value *j = json_parse(responseBody.c_str(),responseBody.length());
|
if (j.is_array()) {
|
||||||
if (j) {
|
for(unsigned long k=0;k<j.size();++k) {
|
||||||
if (j->type == json_array) {
|
nlohmann::json &p = j[k];
|
||||||
for(unsigned int p=0;p<j->u.array.length;++p) {
|
std::string bestPath;
|
||||||
json_value *jp = j->u.array.values[p];
|
nlohmann::json &paths = p["paths"];
|
||||||
if (jp->type == json_object) {
|
if (paths.is_array()) {
|
||||||
const char *address = (const char *)0;
|
for(unsigned long i=0;i<paths.size();++i) {
|
||||||
std::string paths;
|
nlohmann::json &path = paths[i];
|
||||||
int64_t latency = 0;
|
if (path["preferred"]) {
|
||||||
int64_t versionMajor = -1,versionMinor = -1,versionRev = -1;
|
char tmp[256];
|
||||||
const char *role = (const char *)0;
|
std::string addr = path["address"];
|
||||||
for(unsigned int k=0;k<jp->u.object.length;++k) {
|
const uint64_t now = OSUtils::now();
|
||||||
if ((!strcmp(jp->u.object.values[k].name,"address"))&&(jp->u.object.values[k].value->type == json_string))
|
const double lq = (path.count("linkQuality")) ? (double)path["linkQuality"] : -1.0;
|
||||||
address = jp->u.object.values[k].value->u.string.ptr;
|
Utils::snprintf(tmp,sizeof(tmp),"%s;%llu;%llu;%1.2f",addr.c_str(),now - (uint64_t)path["lastSend"],now - (uint64_t)path["lastReceive"],lq);
|
||||||
else if ((!strcmp(jp->u.object.values[k].name,"versionMajor"))&&(jp->u.object.values[k].value->type == json_integer))
|
bestPath = tmp;
|
||||||
versionMajor = jp->u.object.values[k].value->u.integer;
|
break;
|
||||||
else if ((!strcmp(jp->u.object.values[k].name,"versionMinor"))&&(jp->u.object.values[k].value->type == json_integer))
|
|
||||||
versionMinor = jp->u.object.values[k].value->u.integer;
|
|
||||||
else if ((!strcmp(jp->u.object.values[k].name,"versionRev"))&&(jp->u.object.values[k].value->type == json_integer))
|
|
||||||
versionRev = jp->u.object.values[k].value->u.integer;
|
|
||||||
else if ((!strcmp(jp->u.object.values[k].name,"role"))&&(jp->u.object.values[k].value->type == json_string))
|
|
||||||
role = jp->u.object.values[k].value->u.string.ptr;
|
|
||||||
else if ((!strcmp(jp->u.object.values[k].name,"latency"))&&(jp->u.object.values[k].value->type == json_integer))
|
|
||||||
latency = jp->u.object.values[k].value->u.integer;
|
|
||||||
else if ((!strcmp(jp->u.object.values[k].name,"paths"))&&(jp->u.object.values[k].value->type == json_array)) {
|
|
||||||
for(unsigned int pp=0;pp<jp->u.object.values[k].value->u.array.length;++pp) {
|
|
||||||
json_value *jpath = jp->u.object.values[k].value->u.array.values[pp];
|
|
||||||
if (jpath->type == json_object) {
|
|
||||||
const char *paddr = (const char *)0;
|
|
||||||
int64_t lastSend = 0;
|
|
||||||
int64_t lastReceive = 0;
|
|
||||||
bool preferred = false;
|
|
||||||
bool active = false;
|
|
||||||
for(unsigned int kk=0;kk<jpath->u.object.length;++kk) {
|
|
||||||
if ((!strcmp(jpath->u.object.values[kk].name,"address"))&&(jpath->u.object.values[kk].value->type == json_string))
|
|
||||||
paddr = jpath->u.object.values[kk].value->u.string.ptr;
|
|
||||||
else if ((!strcmp(jpath->u.object.values[kk].name,"lastSend"))&&(jpath->u.object.values[kk].value->type == json_integer))
|
|
||||||
lastSend = jpath->u.object.values[kk].value->u.integer;
|
|
||||||
else if ((!strcmp(jpath->u.object.values[kk].name,"lastReceive"))&&(jpath->u.object.values[kk].value->type == json_integer))
|
|
||||||
lastReceive = jpath->u.object.values[kk].value->u.integer;
|
|
||||||
else if ((!strcmp(jpath->u.object.values[kk].name,"preferred"))&&(jpath->u.object.values[kk].value->type == json_boolean))
|
|
||||||
preferred = (jpath->u.object.values[kk].value->u.boolean != 0);
|
|
||||||
else if ((!strcmp(jpath->u.object.values[kk].name,"active"))&&(jpath->u.object.values[kk].value->type == json_boolean))
|
|
||||||
active = (jpath->u.object.values[kk].value->u.boolean != 0);
|
|
||||||
}
|
|
||||||
if ((paddr)&&(active)) {
|
|
||||||
int64_t now = (int64_t)OSUtils::now();
|
|
||||||
if (lastSend > 0)
|
|
||||||
lastSend = now - lastSend;
|
|
||||||
if (lastReceive > 0)
|
|
||||||
lastReceive = now - lastReceive;
|
|
||||||
char pathtmp[256];
|
|
||||||
Utils::snprintf(pathtmp,sizeof(pathtmp),"%s;%lld;%lld;%s",
|
|
||||||
paddr,
|
|
||||||
lastSend,
|
|
||||||
lastReceive,
|
|
||||||
(preferred ? "preferred" : "active"));
|
|
||||||
if (paths.length())
|
|
||||||
paths.push_back(',');
|
|
||||||
paths.append(pathtmp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (bestPath.length() == 0) bestPath = "-";
|
||||||
|
char ver[128];
|
||||||
|
int64_t vmaj = p["versionMajor"];
|
||||||
|
int64_t vmin = p["versionMinor"];
|
||||||
|
int64_t vrev = p["versionRev"];
|
||||||
|
if (vmaj >= 0) {
|
||||||
|
Utils::snprintf(ver,sizeof(ver),"%lld.%lld.%lld",vmaj,vmin,vrev);
|
||||||
|
} else {
|
||||||
|
ver[0] = '-';
|
||||||
|
ver[1] = (char)0;
|
||||||
|
}
|
||||||
|
printf("200 listpeers %s %s %d %s %s" ZT_EOL_S,
|
||||||
|
OSUtils::jsonString(p["address"],"-").c_str(),
|
||||||
|
bestPath.c_str(),
|
||||||
|
(int)OSUtils::jsonInt(p["latency"],0),
|
||||||
|
ver,
|
||||||
|
OSUtils::jsonString(p["role"],"-").c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((address)&&(role)) {
|
|
||||||
char verstr[64];
|
|
||||||
if ((versionMajor >= 0)&&(versionMinor >= 0)&&(versionRev >= 0))
|
|
||||||
Utils::snprintf(verstr,sizeof(verstr),"%lld.%lld.%lld",versionMajor,versionMinor,versionRev);
|
|
||||||
else {
|
|
||||||
verstr[0] = '-';
|
|
||||||
verstr[1] = (char)0;
|
|
||||||
}
|
|
||||||
printf("200 listpeers %s %s %lld %s %s" ZT_EOL_S,address,(paths.length()) ? paths.c_str() : "-",(long long)latency,verstr,role);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
json_value_free(j);
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
|
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else if (command == "listnetworks") {
|
} else if (command == "listnetworks") {
|
||||||
unsigned int scode = Http::GET(
|
const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/network",requestHeaders,responseHeaders,responseBody);
|
||||||
1024 * 1024 * 16,
|
|
||||||
60000,
|
nlohmann::json j;
|
||||||
(const struct sockaddr *)&addr,
|
try {
|
||||||
"/network",
|
j = OSUtils::jsonParse(responseBody);
|
||||||
requestHeaders,
|
} catch (std::exception &exc) {
|
||||||
responseHeaders,
|
printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
|
||||||
responseBody);
|
return 1;
|
||||||
|
} catch ( ... ) {
|
||||||
|
printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (scode == 200) {
|
if (scode == 200) {
|
||||||
if (json) {
|
if (json) {
|
||||||
printf("%s",cliFixJsonCRs(responseBody).c_str());
|
printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
|
||||||
return 0;
|
|
||||||
} else {
|
} else {
|
||||||
printf("200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>" ZT_EOL_S);
|
printf("200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>" ZT_EOL_S);
|
||||||
json_value *j = json_parse(responseBody.c_str(),responseBody.length());
|
if (j.is_array()) {
|
||||||
if (j) {
|
for(unsigned long i=0;i<j.size();++i) {
|
||||||
if (j->type == json_array) {
|
nlohmann::json &n = j[i];
|
||||||
for(unsigned int p=0;p<j->u.array.length;++p) {
|
if (n.is_object()) {
|
||||||
json_value *jn = j->u.array.values[p];
|
std::string aa;
|
||||||
if (jn->type == json_object) {
|
nlohmann::json &assignedAddresses = n["assignedAddresses"];
|
||||||
const char *nwid = (const char *)0;
|
if (assignedAddresses.is_array()) {
|
||||||
const char *name = "";
|
for(unsigned long j=0;j<assignedAddresses.size();++j) {
|
||||||
const char *mac = (const char *)0;
|
nlohmann::json &addr = assignedAddresses[j];
|
||||||
const char *status = (const char *)0;
|
if (addr.is_string()) {
|
||||||
const char *type = (const char *)0;
|
if (aa.length() > 0) aa.push_back(',');
|
||||||
const char *portDeviceName = "";
|
aa.append(addr.get<std::string>());
|
||||||
std::string ips;
|
|
||||||
for(unsigned int k=0;k<jn->u.object.length;++k) {
|
|
||||||
if ((!strcmp(jn->u.object.values[k].name,"nwid"))&&(jn->u.object.values[k].value->type == json_string))
|
|
||||||
nwid = jn->u.object.values[k].value->u.string.ptr;
|
|
||||||
else if ((!strcmp(jn->u.object.values[k].name,"name"))&&(jn->u.object.values[k].value->type == json_string))
|
|
||||||
name = jn->u.object.values[k].value->u.string.ptr;
|
|
||||||
else if ((!strcmp(jn->u.object.values[k].name,"mac"))&&(jn->u.object.values[k].value->type == json_string))
|
|
||||||
mac = jn->u.object.values[k].value->u.string.ptr;
|
|
||||||
else if ((!strcmp(jn->u.object.values[k].name,"status"))&&(jn->u.object.values[k].value->type == json_string))
|
|
||||||
status = jn->u.object.values[k].value->u.string.ptr;
|
|
||||||
else if ((!strcmp(jn->u.object.values[k].name,"type"))&&(jn->u.object.values[k].value->type == json_string))
|
|
||||||
type = jn->u.object.values[k].value->u.string.ptr;
|
|
||||||
else if ((!strcmp(jn->u.object.values[k].name,"portDeviceName"))&&(jn->u.object.values[k].value->type == json_string))
|
|
||||||
portDeviceName = jn->u.object.values[k].value->u.string.ptr;
|
|
||||||
else if ((!strcmp(jn->u.object.values[k].name,"assignedAddresses"))&&(jn->u.object.values[k].value->type == json_array)) {
|
|
||||||
for(unsigned int a=0;a<jn->u.object.values[k].value->u.array.length;++a) {
|
|
||||||
json_value *aa = jn->u.object.values[k].value->u.array.values[a];
|
|
||||||
if (aa->type == json_string) {
|
|
||||||
if (ips.length())
|
|
||||||
ips.push_back(',');
|
|
||||||
ips.append(aa->u.string.ptr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (aa.length() == 0) aa = "-";
|
||||||
if ((nwid)&&(mac)&&(status)&&(type)) {
|
|
||||||
printf("200 listnetworks %s %s %s %s %s %s %s" ZT_EOL_S,
|
printf("200 listnetworks %s %s %s %s %s %s %s" ZT_EOL_S,
|
||||||
nwid,
|
OSUtils::jsonString(n["nwid"],"-").c_str(),
|
||||||
(((name)&&(name[0])) ? name : "-"),
|
OSUtils::jsonString(n["name"],"-").c_str(),
|
||||||
mac,
|
OSUtils::jsonString(n["mac"],"-").c_str(),
|
||||||
status,
|
OSUtils::jsonString(n["status"],"-").c_str(),
|
||||||
type,
|
OSUtils::jsonString(n["type"],"-").c_str(),
|
||||||
(((portDeviceName)&&(portDeviceName[0])) ? portDeviceName : "-"),
|
OSUtils::jsonString(n["portDeviceName"],"-").c_str(),
|
||||||
((ips.length() > 0) ? ips.c_str() : "-"));
|
aa.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
json_value_free(j);
|
return 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
|
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
|
||||||
return 1;
|
return 1;
|
||||||
@@ -554,6 +491,75 @@ static int cli(int argc,char **argv)
|
|||||||
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
|
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
} else if (command == "listmoons") {
|
||||||
|
const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/moon",requestHeaders,responseHeaders,responseBody);
|
||||||
|
|
||||||
|
nlohmann::json j;
|
||||||
|
try {
|
||||||
|
j = OSUtils::jsonParse(responseBody);
|
||||||
|
} catch (std::exception &exc) {
|
||||||
|
printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
|
||||||
|
return 1;
|
||||||
|
} catch ( ... ) {
|
||||||
|
printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scode == 200) {
|
||||||
|
printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else if (command == "orbit") {
|
||||||
|
const uint64_t worldId = Utils::hexStrToU64(arg1.c_str());
|
||||||
|
const uint64_t seed = Utils::hexStrToU64(arg2.c_str());
|
||||||
|
if ((worldId)&&(seed)) {
|
||||||
|
char jsons[1024];
|
||||||
|
Utils::snprintf(jsons,sizeof(jsons),"{\"seed\":\"%s\"}",arg2.c_str());
|
||||||
|
char cl[128];
|
||||||
|
Utils::snprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
|
||||||
|
requestHeaders["Content-Type"] = "application/json";
|
||||||
|
requestHeaders["Content-Length"] = cl;
|
||||||
|
unsigned int scode = Http::POST(
|
||||||
|
1024 * 1024 * 16,
|
||||||
|
60000,
|
||||||
|
(const struct sockaddr *)&addr,
|
||||||
|
(std::string("/moon/") + arg1).c_str(),
|
||||||
|
requestHeaders,
|
||||||
|
jsons,
|
||||||
|
(unsigned long)strlen(jsons),
|
||||||
|
responseHeaders,
|
||||||
|
responseBody);
|
||||||
|
if (scode == 200) {
|
||||||
|
printf("200 orbit OK" ZT_EOL_S);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (command == "deorbit") {
|
||||||
|
unsigned int scode = Http::DEL(
|
||||||
|
1024 * 1024 * 16,
|
||||||
|
60000,
|
||||||
|
(const struct sockaddr *)&addr,
|
||||||
|
(std::string("/moon/") + arg1).c_str(),
|
||||||
|
requestHeaders,
|
||||||
|
responseHeaders,
|
||||||
|
responseBody);
|
||||||
|
if (scode == 200) {
|
||||||
|
if (json) {
|
||||||
|
printf("%s",cliFixJsonCRs(responseBody).c_str());
|
||||||
|
} else {
|
||||||
|
printf("200 deorbit OK" ZT_EOL_S);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
} else if (command == "set") {
|
} else if (command == "set") {
|
||||||
if (arg1.length() != 16) {
|
if (arg1.length() != 16) {
|
||||||
cliPrintHelp(argv[0],stderr);
|
cliPrintHelp(argv[0],stderr);
|
||||||
@@ -577,7 +583,7 @@ static int cli(int argc,char **argv)
|
|||||||
(std::string("/network/") + arg1).c_str(),
|
(std::string("/network/") + arg1).c_str(),
|
||||||
requestHeaders,
|
requestHeaders,
|
||||||
jsons,
|
jsons,
|
||||||
strlen(jsons),
|
(unsigned long)strlen(jsons),
|
||||||
responseHeaders,
|
responseHeaders,
|
||||||
responseBody);
|
responseBody);
|
||||||
if (scode == 200) {
|
if (scode == 200) {
|
||||||
@@ -619,7 +625,8 @@ static void idtoolPrintHelp(FILE *out,const char *pn)
|
|||||||
fprintf(out," getpublic <identity.secret>" ZT_EOL_S);
|
fprintf(out," getpublic <identity.secret>" ZT_EOL_S);
|
||||||
fprintf(out," sign <identity.secret> <file>" ZT_EOL_S);
|
fprintf(out," sign <identity.secret> <file>" ZT_EOL_S);
|
||||||
fprintf(out," verify <identity.secret/public> <file> <signature>" ZT_EOL_S);
|
fprintf(out," verify <identity.secret/public> <file> <signature>" ZT_EOL_S);
|
||||||
fprintf(out," mkcom <identity.secret> [<id,value,maxDelta> ...] (hexadecimal integers)" ZT_EOL_S);
|
fprintf(out," initmoon <identity.public of first seed>" ZT_EOL_S);
|
||||||
|
fprintf(out," genmoon <moon json>" ZT_EOL_S);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Identity getIdFromArg(char *arg)
|
static Identity getIdFromArg(char *arg)
|
||||||
@@ -654,7 +661,7 @@ static int idtool(int argc,char **argv)
|
|||||||
int vanityBits = 0;
|
int vanityBits = 0;
|
||||||
if (argc >= 5) {
|
if (argc >= 5) {
|
||||||
vanity = Utils::hexStrToU64(argv[4]) & 0xffffffffffULL;
|
vanity = Utils::hexStrToU64(argv[4]) & 0xffffffffffULL;
|
||||||
vanityBits = 4 * strlen(argv[4]);
|
vanityBits = 4 * (int)strlen(argv[4]);
|
||||||
if (vanityBits > 40)
|
if (vanityBits > 40)
|
||||||
vanityBits = 40;
|
vanityBits = 40;
|
||||||
}
|
}
|
||||||
@@ -764,34 +771,91 @@ static int idtool(int argc,char **argv)
|
|||||||
fprintf(stderr,"%s signature check FAILED" ZT_EOL_S,argv[3]);
|
fprintf(stderr,"%s signature check FAILED" ZT_EOL_S,argv[3]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else if (!strcmp(argv[1],"mkcom")) {
|
} else if (!strcmp(argv[1],"initmoon")) {
|
||||||
if (argc < 3) {
|
if (argc < 3) {
|
||||||
idtoolPrintHelp(stdout,argv[0]);
|
idtoolPrintHelp(stdout,argv[0]);
|
||||||
|
} else {
|
||||||
|
const Identity id = getIdFromArg(argv[2]);
|
||||||
|
if (!id) {
|
||||||
|
fprintf(stderr,"%s is not a valid identity" ZT_EOL_S,argv[2]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Identity id = getIdFromArg(argv[2]);
|
C25519::Pair kp(C25519::generate());
|
||||||
if ((!id)||(!id.hasPrivate())) {
|
|
||||||
fprintf(stderr,"Identity argument invalid, does not include private key, or file unreadable: %s" ZT_EOL_S,argv[2]);
|
nlohmann::json mj;
|
||||||
|
mj["objtype"] = "world";
|
||||||
|
mj["worldType"] = "moon";
|
||||||
|
mj["updatesMustBeSignedBy"] = mj["signingKey"] = Utils::hex(kp.pub.data,(unsigned int)kp.pub.size());
|
||||||
|
mj["updatesMustBeSignedBy_SECRET"] = Utils::hex(kp.priv.data,(unsigned int)kp.priv.size());
|
||||||
|
mj["id"] = id.address().toString();
|
||||||
|
nlohmann::json seedj;
|
||||||
|
seedj["identity"] = id.toString(false);
|
||||||
|
seedj["stableEndpoints"] = nlohmann::json::array();
|
||||||
|
(mj["roots"] = nlohmann::json::array()).push_back(seedj);
|
||||||
|
std::string mjd(OSUtils::jsonDump(mj));
|
||||||
|
|
||||||
|
printf("%s" ZT_EOL_S,mjd.c_str());
|
||||||
|
}
|
||||||
|
} else if (!strcmp(argv[1],"genmoon")) {
|
||||||
|
if (argc < 3) {
|
||||||
|
idtoolPrintHelp(stdout,argv[0]);
|
||||||
|
} else {
|
||||||
|
std::string buf;
|
||||||
|
if (!OSUtils::readFile(argv[2],buf)) {
|
||||||
|
fprintf(stderr,"cannot read %s" ZT_EOL_S,argv[2]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
nlohmann::json mj(OSUtils::jsonParse(buf));
|
||||||
|
|
||||||
|
const uint64_t id = Utils::hexStrToU64(OSUtils::jsonString(mj["id"],"0").c_str());
|
||||||
|
if (!id) {
|
||||||
|
fprintf(stderr,"ID in %s is invalid" ZT_EOL_S,argv[2]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
CertificateOfMembership com;
|
World::Type t;
|
||||||
for(int a=3;a<argc;++a) {
|
if (mj["worldType"] == "moon") {
|
||||||
std::vector<std::string> params(Utils::split(argv[a],",","",""));
|
t = World::TYPE_MOON;
|
||||||
if (params.size() == 3) {
|
} else if (mj["worldType"] == "planet") {
|
||||||
uint64_t qId = Utils::hexStrToU64(params[0].c_str());
|
t = World::TYPE_PLANET;
|
||||||
uint64_t qValue = Utils::hexStrToU64(params[1].c_str());
|
} else {
|
||||||
uint64_t qMaxDelta = Utils::hexStrToU64(params[2].c_str());
|
fprintf(stderr,"invalid worldType" ZT_EOL_S);
|
||||||
com.setQualifier(qId,qValue,qMaxDelta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!com.sign(id)) {
|
|
||||||
fprintf(stderr,"Signature of certificate of membership failed." ZT_EOL_S);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s",com.toString().c_str());
|
C25519::Pair signingKey;
|
||||||
|
Utils::unhex(OSUtils::jsonString(mj["updatesMustBeSignedBy"],""),signingKey.pub.data,(unsigned int)signingKey.pub.size());
|
||||||
|
Utils::unhex(OSUtils::jsonString(mj["updatesMustBeSignedBy_SECRET"],""),signingKey.priv.data,(unsigned int)signingKey.priv.size());
|
||||||
|
|
||||||
|
std::vector<World::Root> roots;
|
||||||
|
nlohmann::json &rootsj = mj["roots"];
|
||||||
|
if (rootsj.is_array()) {
|
||||||
|
for(unsigned long i=0;i<(unsigned long)rootsj.size();++i) {
|
||||||
|
nlohmann::json &r = rootsj[i];
|
||||||
|
if (r.is_object()) {
|
||||||
|
roots.push_back(World::Root());
|
||||||
|
roots.back().identity = Identity(OSUtils::jsonString(r["identity"],""));
|
||||||
|
nlohmann::json &stableEndpointsj = r["stableEndpoints"];
|
||||||
|
if (stableEndpointsj.is_array()) {
|
||||||
|
for(unsigned long k=0;k<(unsigned long)stableEndpointsj.size();++k)
|
||||||
|
roots.back().stableEndpoints.push_back(InetAddress(OSUtils::jsonString(stableEndpointsj[k],"")));
|
||||||
|
std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::sort(roots.begin(),roots.end());
|
||||||
|
|
||||||
|
const uint64_t now = OSUtils::now();
|
||||||
|
World w(World::make(t,id,now,signingKey.pub,roots,signingKey));
|
||||||
|
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> wbuf;
|
||||||
|
w.serialize(wbuf);
|
||||||
|
char fn[128];
|
||||||
|
Utils::snprintf(fn,sizeof(fn),"%.16llx_%.16llx.moon",w.id(),now);
|
||||||
|
OSUtils::writeFile(fn,wbuf.data(),wbuf.size());
|
||||||
|
printf("wrote %s (signed world with timestamp %llu)" ZT_EOL_S,fn,(unsigned long long)now);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
idtoolPrintHelp(stdout,argv[0]);
|
idtoolPrintHelp(stdout,argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -817,6 +881,147 @@ static void _sighandlerQuit(int sig)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Drop privileges on Linux, if supported by libc etc. and "zerotier-one" user exists on system
|
||||||
|
#ifdef __LINUX__
|
||||||
|
#ifndef PR_CAP_AMBIENT
|
||||||
|
#define PR_CAP_AMBIENT 47
|
||||||
|
#define PR_CAP_AMBIENT_IS_SET 1
|
||||||
|
#define PR_CAP_AMBIENT_RAISE 2
|
||||||
|
#define PR_CAP_AMBIENT_LOWER 3
|
||||||
|
#define PR_CAP_AMBIENT_CLEAR_ALL 4
|
||||||
|
#endif
|
||||||
|
#define ZT_LINUX_USER "zerotier-one"
|
||||||
|
#define ZT_HAVE_DROP_PRIVILEGES 1
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// libc doesn't export capset, it is instead located in libcap
|
||||||
|
// We ignore libcap and call it manually.
|
||||||
|
struct cap_header_struct {
|
||||||
|
__u32 version;
|
||||||
|
int pid;
|
||||||
|
};
|
||||||
|
struct cap_data_struct {
|
||||||
|
__u32 effective;
|
||||||
|
__u32 permitted;
|
||||||
|
__u32 inheritable;
|
||||||
|
};
|
||||||
|
static inline int _zt_capset(cap_header_struct* hdrp, cap_data_struct* datap) { return syscall(SYS_capset, hdrp, datap); }
|
||||||
|
|
||||||
|
static void _notDropping(const char *procName,const std::string &homeDir)
|
||||||
|
{
|
||||||
|
struct stat buf;
|
||||||
|
if (lstat(homeDir.c_str(),&buf) < 0) {
|
||||||
|
if (buf.st_uid != 0 || buf.st_gid != 0) {
|
||||||
|
fprintf(stderr, "%s: FATAL: failed to drop privileges and can't run as root since privileges were previously dropped (home directory not owned by root)" ZT_EOL_S,procName);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%s: WARNING: failed to drop privileges (kernel may not support required prctl features), running as root" ZT_EOL_S,procName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _setCapabilities(int flags)
|
||||||
|
{
|
||||||
|
cap_header_struct capheader = {_LINUX_CAPABILITY_VERSION_1, 0};
|
||||||
|
cap_data_struct capdata;
|
||||||
|
capdata.inheritable = capdata.permitted = capdata.effective = flags;
|
||||||
|
return _zt_capset(&capheader, &capdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _recursiveChown(const char *path,uid_t uid,gid_t gid)
|
||||||
|
{
|
||||||
|
struct dirent de;
|
||||||
|
struct dirent *dptr;
|
||||||
|
lchown(path,uid,gid);
|
||||||
|
DIR *d = opendir(path);
|
||||||
|
if (!d)
|
||||||
|
return;
|
||||||
|
dptr = (struct dirent *)0;
|
||||||
|
for(;;) {
|
||||||
|
if (readdir_r(d,&de,&dptr) != 0)
|
||||||
|
break;
|
||||||
|
if (!dptr)
|
||||||
|
break;
|
||||||
|
if ((strcmp(dptr->d_name,".") != 0)&&(strcmp(dptr->d_name,"..") != 0)&&(strlen(dptr->d_name) > 0)) {
|
||||||
|
std::string p(path);
|
||||||
|
p.push_back(ZT_PATH_SEPARATOR);
|
||||||
|
p.append(dptr->d_name);
|
||||||
|
_recursiveChown(p.c_str(),uid,gid); // will just fail and return on regular files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dropPrivileges(const char *procName,const std::string &homeDir)
|
||||||
|
{
|
||||||
|
if (getuid() != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// dropPrivileges switches to zerotier-one user while retaining CAP_NET_ADMIN
|
||||||
|
// and CAP_NET_RAW capabilities.
|
||||||
|
struct passwd *targetUser = getpwnam(ZT_LINUX_USER);
|
||||||
|
if (!targetUser)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_RAW, 0, 0) < 0) {
|
||||||
|
// Kernel has no support for ambient capabilities.
|
||||||
|
_notDropping(procName,homeDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS | SECBIT_NOROOT) < 0) {
|
||||||
|
_notDropping(procName,homeDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change ownership of our home directory if everything looks good (does nothing if already chown'd)
|
||||||
|
_recursiveChown(homeDir.c_str(),targetUser->pw_uid,targetUser->pw_gid);
|
||||||
|
|
||||||
|
if (_setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID) | (1 << CAP_SETGID)) < 0) {
|
||||||
|
_notDropping(procName,homeDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int oldDumpable = prctl(PR_GET_DUMPABLE);
|
||||||
|
if (prctl(PR_SET_DUMPABLE, 0) < 0) {
|
||||||
|
// Disable ptracing. Otherwise there is a small window when previous
|
||||||
|
// compromised ZeroTier process could ptrace us, when we still have CAP_SETUID.
|
||||||
|
// (this is mitigated anyway on most distros by ptrace_scope=1)
|
||||||
|
fprintf(stderr,"%s: FATAL: prctl(PR_SET_DUMPABLE) failed while attempting to relinquish root permissions" ZT_EOL_S,procName);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relinquish root
|
||||||
|
if (setgid(targetUser->pw_gid) < 0) {
|
||||||
|
perror("setgid");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (setuid(targetUser->pw_uid) < 0) {
|
||||||
|
perror("setuid");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW)) < 0) {
|
||||||
|
fprintf(stderr,"%s: FATAL: unable to drop capabilities after relinquishing root" ZT_EOL_S,procName);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prctl(PR_SET_DUMPABLE, oldDumpable) < 0) {
|
||||||
|
fprintf(stderr,"%s: FATAL: prctl(PR_SET_DUMPABLE) failed while attempting to relinquish root permissions" ZT_EOL_S,procName);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_ADMIN, 0, 0) < 0) {
|
||||||
|
fprintf(stderr,"%s: FATAL: prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_RAISE,CAP_NET_ADMIN) failed while attempting to relinquish root permissions" ZT_EOL_S,procName);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_RAW, 0, 0) < 0) {
|
||||||
|
fprintf(stderr,"%s: FATAL: prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_RAISE,CAP_NET_RAW) failed while attempting to relinquish root permissions" ZT_EOL_S,procName);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
#endif // __LINUX__
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
/* Windows helper functions and signal handlers */
|
/* Windows helper functions and signal handlers */
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
@@ -979,14 +1184,11 @@ static void printHelp(const char *cn,FILE *out)
|
|||||||
fprintf(out,
|
fprintf(out,
|
||||||
COPYRIGHT_NOTICE ZT_EOL_S
|
COPYRIGHT_NOTICE ZT_EOL_S
|
||||||
LICENSE_GRANT ZT_EOL_S);
|
LICENSE_GRANT ZT_EOL_S);
|
||||||
std::string updateUrl(OneService::autoUpdateUrl());
|
|
||||||
if (updateUrl.length())
|
|
||||||
fprintf(out,"Automatic updates enabled:" ZT_EOL_S" %s" ZT_EOL_S" (all updates are securely authenticated by 256-bit ECDSA signature)" ZT_EOL_S"" ZT_EOL_S,updateUrl.c_str());
|
|
||||||
fprintf(out,"Usage: %s [-switches] [home directory]" ZT_EOL_S"" ZT_EOL_S,cn);
|
fprintf(out,"Usage: %s [-switches] [home directory]" ZT_EOL_S"" ZT_EOL_S,cn);
|
||||||
fprintf(out,"Available switches:" ZT_EOL_S);
|
fprintf(out,"Available switches:" ZT_EOL_S);
|
||||||
fprintf(out," -h - Display this help" ZT_EOL_S);
|
fprintf(out," -h - Display this help" ZT_EOL_S);
|
||||||
fprintf(out," -v - Show version" ZT_EOL_S);
|
fprintf(out," -v - Show version" ZT_EOL_S);
|
||||||
fprintf(out," -U - Run as unprivileged user (skip privilege check)" ZT_EOL_S);
|
fprintf(out," -U - Skip privilege check and do not attempt to drop privileges" ZT_EOL_S);
|
||||||
fprintf(out," -p<port> - Port for UDP and TCP/HTTP (default: 9993, 0 for random)" ZT_EOL_S);
|
fprintf(out," -p<port> - Port for UDP and TCP/HTTP (default: 9993, 0 for random)" ZT_EOL_S);
|
||||||
|
|
||||||
#ifdef __UNIX_LIKE__
|
#ifdef __UNIX_LIKE__
|
||||||
@@ -1157,7 +1359,7 @@ int main(int argc,char **argv)
|
|||||||
fprintf(stderr,"%s: no home path specified and no platform default available" ZT_EOL_S,argv[0]);
|
fprintf(stderr,"%s: no home path specified and no platform default available" ZT_EOL_S,argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
std::vector<std::string> hpsp(Utils::split(homeDir.c_str(),ZT_PATH_SEPARATOR_S,"",""));
|
std::vector<std::string> hpsp(OSUtils::split(homeDir.c_str(),ZT_PATH_SEPARATOR_S,"",""));
|
||||||
std::string ptmp;
|
std::string ptmp;
|
||||||
if (homeDir[0] == ZT_PATH_SEPARATOR)
|
if (homeDir[0] == ZT_PATH_SEPARATOR)
|
||||||
ptmp.push_back(ZT_PATH_SEPARATOR);
|
ptmp.push_back(ZT_PATH_SEPARATOR);
|
||||||
@@ -1172,6 +1374,12 @@ int main(int argc,char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This can be removed once the new controller code has been around for many versions
|
||||||
|
if (OSUtils::fileExists((homeDir + ZT_PATH_SEPARATOR_S + "controller.db").c_str(),true)) {
|
||||||
|
fprintf(stderr,"%s: FATAL: an old controller.db exists in %s -- see instructions in controller/README.md for how to migrate!" ZT_EOL_S,argv[0],homeDir.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __UNIX_LIKE__
|
#ifdef __UNIX_LIKE__
|
||||||
#ifndef ZT_ONE_NO_ROOT_CHECK
|
#ifndef ZT_ONE_NO_ROOT_CHECK
|
||||||
if ((!skipRootCheck)&&(getuid() != 0)) {
|
if ((!skipRootCheck)&&(getuid() != 0)) {
|
||||||
@@ -1221,6 +1429,11 @@ int main(int argc,char **argv)
|
|||||||
#endif // __WINDOWS__
|
#endif // __WINDOWS__
|
||||||
|
|
||||||
#ifdef __UNIX_LIKE__
|
#ifdef __UNIX_LIKE__
|
||||||
|
|
||||||
|
#ifdef ZT_HAVE_DROP_PRIVILEGES
|
||||||
|
dropPrivileges(argv[0],homeDir);
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string pidPath(homeDir + ZT_PATH_SEPARATOR_S + ZT_PID_PATH);
|
std::string pidPath(homeDir + ZT_PATH_SEPARATOR_S + ZT_PID_PATH);
|
||||||
{
|
{
|
||||||
// Write .pid file to home folder
|
// Write .pid file to home folder
|
||||||
@@ -1262,9 +1475,5 @@ int main(int argc,char **argv)
|
|||||||
delete zt1Service;
|
delete zt1Service;
|
||||||
zt1Service = (OneService *)0;
|
zt1Service = (OneService *)0;
|
||||||
|
|
||||||
#ifdef __UNIX_LIKE__
|
|
||||||
OSUtils::rm(pidPath.c_str());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,10 +83,15 @@ BSDEthernetTap::BSDEthernetTap(
|
|||||||
{
|
{
|
||||||
static Mutex globalTapCreateLock;
|
static Mutex globalTapCreateLock;
|
||||||
char devpath[64],ethaddr[64],mtustr[32],metstr[32],tmpdevname[32];
|
char devpath[64],ethaddr[64],mtustr[32],metstr[32],tmpdevname[32];
|
||||||
struct stat stattmp;
|
|
||||||
|
|
||||||
// On FreeBSD at least we can rename, so use nwid to generate a deterministic unique zt#### name using base32
|
Mutex::Lock _gl(globalTapCreateLock);
|
||||||
// As a result we don't use desiredDevice
|
|
||||||
|
if (mtu > 2800)
|
||||||
|
throw std::runtime_error("max tap MTU is 2800");
|
||||||
|
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
/* FreeBSD allows long interface names and interface renaming */
|
||||||
|
|
||||||
_dev = "zt";
|
_dev = "zt";
|
||||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 60) & 0x1f)]);
|
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 60) & 0x1f)]);
|
||||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 55) & 0x1f)]);
|
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 55) & 0x1f)]);
|
||||||
@@ -102,13 +107,6 @@ BSDEthernetTap::BSDEthernetTap(
|
|||||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 5) & 0x1f)]);
|
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 5) & 0x1f)]);
|
||||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)(nwid & 0x1f)]);
|
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)(nwid & 0x1f)]);
|
||||||
|
|
||||||
Mutex::Lock _gl(globalTapCreateLock);
|
|
||||||
|
|
||||||
if (mtu > 2800)
|
|
||||||
throw std::runtime_error("max tap MTU is 2800");
|
|
||||||
|
|
||||||
// On BSD we create taps and they can have high numbers, so use ones starting
|
|
||||||
// at 9993 to not conflict with other stuff. Then we rename it to zt<base32 of nwid>
|
|
||||||
std::vector<std::string> devFiles(OSUtils::listDirectory("/dev"));
|
std::vector<std::string> devFiles(OSUtils::listDirectory("/dev"));
|
||||||
for(int i=9993;i<(9993+128);++i) {
|
for(int i=9993;i<(9993+128);++i) {
|
||||||
Utils::snprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
|
Utils::snprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
|
||||||
@@ -123,6 +121,7 @@ BSDEthernetTap::BSDEthernetTap(
|
|||||||
::waitpid(cpid,&exitcode,0);
|
::waitpid(cpid,&exitcode,0);
|
||||||
} else throw std::runtime_error("fork() failed");
|
} else throw std::runtime_error("fork() failed");
|
||||||
|
|
||||||
|
struct stat stattmp;
|
||||||
if (!stat(devpath,&stattmp)) {
|
if (!stat(devpath,&stattmp)) {
|
||||||
cpid = (long)vfork();
|
cpid = (long)vfork();
|
||||||
if (cpid == 0) {
|
if (cpid == 0) {
|
||||||
@@ -144,6 +143,19 @@ BSDEthernetTap::BSDEthernetTap(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
/* Other BSDs like OpenBSD only have a limited number of tap devices that cannot be renamed */
|
||||||
|
|
||||||
|
for(int i=0;i<64;++i) {
|
||||||
|
Utils::snprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
|
||||||
|
Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
|
||||||
|
_fd = ::open(devpath,O_RDWR);
|
||||||
|
if (_fd > 0) {
|
||||||
|
_dev = tmpdevname;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (_fd <= 0)
|
if (_fd <= 0)
|
||||||
throw std::runtime_error("unable to open TAP device or no more devices available");
|
throw std::runtime_error("unable to open TAP device or no more devices available");
|
||||||
@@ -325,6 +337,7 @@ void BSDEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std:
|
|||||||
{
|
{
|
||||||
std::vector<MulticastGroup> newGroups;
|
std::vector<MulticastGroup> newGroups;
|
||||||
|
|
||||||
|
#ifndef __OpenBSD__
|
||||||
struct ifmaddrs *ifmap = (struct ifmaddrs *)0;
|
struct ifmaddrs *ifmap = (struct ifmaddrs *)0;
|
||||||
if (!getifmaddrs(&ifmap)) {
|
if (!getifmaddrs(&ifmap)) {
|
||||||
struct ifmaddrs *p = ifmap;
|
struct ifmaddrs *p = ifmap;
|
||||||
@@ -339,6 +352,7 @@ void BSDEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std:
|
|||||||
}
|
}
|
||||||
freeifmaddrs(ifmap);
|
freeifmaddrs(ifmap);
|
||||||
}
|
}
|
||||||
|
#endif // __OpenBSD__
|
||||||
|
|
||||||
std::vector<InetAddress> allIps(ips());
|
std::vector<InetAddress> allIps(ips());
|
||||||
for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
|
for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "OSUtils.hpp"
|
|
||||||
#include "Thread.hpp"
|
|
||||||
#include "BackgroundResolver.hpp"
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We can't actually abort a job. This is a legacy characteristic of the
|
|
||||||
* ancient synchronous resolver APIs. So to abort jobs, we just abandon
|
|
||||||
* them by setting their parent to null.
|
|
||||||
*/
|
|
||||||
class BackgroundResolverJob
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::string name;
|
|
||||||
BackgroundResolver *volatile parent;
|
|
||||||
Mutex lock;
|
|
||||||
|
|
||||||
void threadMain()
|
|
||||||
throw()
|
|
||||||
{
|
|
||||||
std::vector<InetAddress> ips;
|
|
||||||
try {
|
|
||||||
ips = OSUtils::resolve(name.c_str());
|
|
||||||
} catch ( ... ) {}
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(lock);
|
|
||||||
BackgroundResolver *p = parent;
|
|
||||||
if (p)
|
|
||||||
p->_postResult(ips);
|
|
||||||
}
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
BackgroundResolver::BackgroundResolver(const char *name) :
|
|
||||||
_name(name),
|
|
||||||
_job((BackgroundResolverJob *)0),
|
|
||||||
_callback(0),
|
|
||||||
_arg((void *)0),
|
|
||||||
_ips(),
|
|
||||||
_lock()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
BackgroundResolver::~BackgroundResolver()
|
|
||||||
{
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<InetAddress> BackgroundResolver::get() const
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
return _ips;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BackgroundResolver::resolveNow(void (*callback)(BackgroundResolver *,void *),void *arg)
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
|
|
||||||
if (_job) {
|
|
||||||
Mutex::Lock _l2(_job->lock);
|
|
||||||
_job->parent = (BackgroundResolver *)0;
|
|
||||||
_job = (BackgroundResolverJob *)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
BackgroundResolverJob *j = new BackgroundResolverJob();
|
|
||||||
j->name = _name;
|
|
||||||
j->parent = this;
|
|
||||||
|
|
||||||
_job = j;
|
|
||||||
_callback = callback;
|
|
||||||
_arg = arg;
|
|
||||||
|
|
||||||
_jobThread = Thread::start(j);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BackgroundResolver::abort()
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
if (_job) {
|
|
||||||
Mutex::Lock _l2(_job->lock);
|
|
||||||
_job->parent = (BackgroundResolver *)0;
|
|
||||||
_job = (BackgroundResolverJob *)0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BackgroundResolver::_postResult(const std::vector<InetAddress> &ips)
|
|
||||||
{
|
|
||||||
void (*cb)(BackgroundResolver *,void *);
|
|
||||||
void *a;
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
_job = (BackgroundResolverJob *)0;
|
|
||||||
cb = _callback;
|
|
||||||
a = _arg;
|
|
||||||
_ips = ips;
|
|
||||||
}
|
|
||||||
if (cb)
|
|
||||||
cb(this,a);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ZT_BACKGROUNDRESOLVER_HPP
|
|
||||||
#define ZT_BACKGROUNDRESOLVER_HPP
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "../node/Constants.hpp"
|
|
||||||
#include "../node/Mutex.hpp"
|
|
||||||
#include "../node/InetAddress.hpp"
|
|
||||||
#include "../node/NonCopyable.hpp"
|
|
||||||
#include "Thread.hpp"
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
class BackgroundResolverJob;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple background resolver
|
|
||||||
*/
|
|
||||||
class BackgroundResolver : NonCopyable
|
|
||||||
{
|
|
||||||
friend class BackgroundResolverJob;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Construct a new resolver
|
|
||||||
*
|
|
||||||
* resolveNow() must be called to actually initiate background resolution.
|
|
||||||
*
|
|
||||||
* @param name Name to resolve
|
|
||||||
*/
|
|
||||||
BackgroundResolver(const char *name);
|
|
||||||
|
|
||||||
~BackgroundResolver();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Most recent resolver results or empty vector if none
|
|
||||||
*/
|
|
||||||
std::vector<InetAddress> get() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Launch a background resolve job now
|
|
||||||
*
|
|
||||||
* If a resolve job is currently in progress, it is aborted and another
|
|
||||||
* job is started.
|
|
||||||
*
|
|
||||||
* Note that jobs can't actually be aborted due to the limitations of the
|
|
||||||
* ancient synchronous OS resolver APIs. As a result, in progress jobs
|
|
||||||
* that are aborted are simply abandoned. Don't call this too frequently
|
|
||||||
* or background threads might pile up.
|
|
||||||
*
|
|
||||||
* @param callback Callback function to receive notification or NULL if none
|
|
||||||
* @praam arg Second argument to callback function
|
|
||||||
*/
|
|
||||||
void resolveNow(void (*callback)(BackgroundResolver *,void *) = 0,void *arg = 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abort (abandon) any current resolve jobs
|
|
||||||
*/
|
|
||||||
void abort();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return True if a background job is in progress
|
|
||||||
*/
|
|
||||||
inline bool running() const
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
return (_job != (BackgroundResolverJob *)0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for pending job to complete (if any)
|
|
||||||
*/
|
|
||||||
inline void wait() const
|
|
||||||
{
|
|
||||||
Thread t;
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
if (!_job)
|
|
||||||
return;
|
|
||||||
t = _jobThread;
|
|
||||||
}
|
|
||||||
Thread::join(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void _postResult(const std::vector<InetAddress> &ips);
|
|
||||||
|
|
||||||
std::string _name;
|
|
||||||
BackgroundResolverJob *_job;
|
|
||||||
Thread _jobThread;
|
|
||||||
void (*_callback)(BackgroundResolver *,void *);
|
|
||||||
void *_arg;
|
|
||||||
std::vector<InetAddress> _ips;
|
|
||||||
Mutex _lock;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -53,8 +53,10 @@
|
|||||||
#include "../node/NonCopyable.hpp"
|
#include "../node/NonCopyable.hpp"
|
||||||
#include "../node/InetAddress.hpp"
|
#include "../node/InetAddress.hpp"
|
||||||
#include "../node/Mutex.hpp"
|
#include "../node/Mutex.hpp"
|
||||||
|
#include "../node/Utils.hpp"
|
||||||
|
|
||||||
#include "Phy.hpp"
|
#include "Phy.hpp"
|
||||||
|
#include "OSUtils.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Period between binder rescans/refreshes
|
* Period between binder rescans/refreshes
|
||||||
@@ -164,7 +166,124 @@ public:
|
|||||||
|
|
||||||
#else // not __WINDOWS__
|
#else // not __WINDOWS__
|
||||||
|
|
||||||
/*
|
/* On Linux we use an alternative method if available since getifaddrs()
|
||||||
|
* gets very slow when there are lots of network namespaces. This won't
|
||||||
|
* work unless /proc/PID/net/if_inet6 exists and it may not on some
|
||||||
|
* embedded systems, so revert to getifaddrs() there. */
|
||||||
|
|
||||||
|
#ifdef __LINUX__
|
||||||
|
char fn[256],tmp[256];
|
||||||
|
std::set<std::string> ifnames;
|
||||||
|
const unsigned long pid = (unsigned long)getpid();
|
||||||
|
|
||||||
|
// Get all device names
|
||||||
|
Utils::snprintf(fn,sizeof(fn),"/proc/%lu/net/dev",pid);
|
||||||
|
FILE *procf = fopen(fn,"r");
|
||||||
|
if (procf) {
|
||||||
|
while (fgets(tmp,sizeof(tmp),procf)) {
|
||||||
|
tmp[255] = 0;
|
||||||
|
char *saveptr = (char *)0;
|
||||||
|
for(char *f=Utils::stok(tmp," \t\r\n:|",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n:|",&saveptr)) {
|
||||||
|
if ((strcmp(f,"Inter-") != 0)&&(strcmp(f,"face") != 0)&&(f[0] != 0))
|
||||||
|
ifnames.insert(f);
|
||||||
|
break; // we only want the first field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(procf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get IPv6 addresses (and any device names we don't already know)
|
||||||
|
Utils::snprintf(fn,sizeof(fn),"/proc/%lu/net/if_inet6",pid);
|
||||||
|
procf = fopen(fn,"r");
|
||||||
|
if (procf) {
|
||||||
|
while (fgets(tmp,sizeof(tmp),procf)) {
|
||||||
|
tmp[255] = 0;
|
||||||
|
char *saveptr = (char *)0;
|
||||||
|
unsigned char ipbits[16];
|
||||||
|
memset(ipbits,0,sizeof(ipbits));
|
||||||
|
char *devname = (char *)0;
|
||||||
|
int n = 0;
|
||||||
|
for(char *f=Utils::stok(tmp," \t\r\n",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n",&saveptr)) {
|
||||||
|
switch(n++) {
|
||||||
|
case 0: // IP in hex
|
||||||
|
Utils::unhex(f,32,ipbits,16);
|
||||||
|
break;
|
||||||
|
case 5: // device name
|
||||||
|
devname = f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (devname) {
|
||||||
|
ifnames.insert(devname);
|
||||||
|
InetAddress ip(ipbits,16,0);
|
||||||
|
if (ifChecker.shouldBindInterface(devname,ip)) {
|
||||||
|
switch(ip.ipScope()) {
|
||||||
|
default: break;
|
||||||
|
case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
|
||||||
|
case InetAddress::IP_SCOPE_GLOBAL:
|
||||||
|
case InetAddress::IP_SCOPE_SHARED:
|
||||||
|
case InetAddress::IP_SCOPE_PRIVATE:
|
||||||
|
ip.setPort(port);
|
||||||
|
localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(devname)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(procf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get IPv4 addresses for each device
|
||||||
|
if (ifnames.size() > 0) {
|
||||||
|
const int controlfd = (int)socket(AF_INET,SOCK_DGRAM,0);
|
||||||
|
struct ifconf configuration;
|
||||||
|
configuration.ifc_len = 0;
|
||||||
|
configuration.ifc_buf = nullptr;
|
||||||
|
|
||||||
|
if (controlfd < 0) goto ip4_address_error;
|
||||||
|
|
||||||
|
if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error;
|
||||||
|
|
||||||
|
configuration.ifc_buf = (char*)malloc(configuration.ifc_len);
|
||||||
|
|
||||||
|
if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error;
|
||||||
|
|
||||||
|
for (int i=0; i < (int)(configuration.ifc_len / sizeof(ifreq)); i ++) {
|
||||||
|
struct ifreq& request = configuration.ifc_req[i];
|
||||||
|
struct sockaddr* addr = &request.ifr_ifru.ifru_addr;
|
||||||
|
if (addr->sa_family != AF_INET) continue;
|
||||||
|
std::string ifname = request.ifr_ifrn.ifrn_name;
|
||||||
|
// name can either be just interface name or interface name followed by ':' and arbitrary label
|
||||||
|
if (ifname.find(':') != std::string::npos) {
|
||||||
|
ifname = ifname.substr(0, ifname.find(':'));
|
||||||
|
}
|
||||||
|
|
||||||
|
InetAddress ip(&(((struct sockaddr_in *)addr)->sin_addr),4,0);
|
||||||
|
if (ifChecker.shouldBindInterface(ifname.c_str(), ip)) {
|
||||||
|
switch(ip.ipScope()) {
|
||||||
|
default: break;
|
||||||
|
case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
|
||||||
|
case InetAddress::IP_SCOPE_GLOBAL:
|
||||||
|
case InetAddress::IP_SCOPE_SHARED:
|
||||||
|
case InetAddress::IP_SCOPE_PRIVATE:
|
||||||
|
ip.setPort(port);
|
||||||
|
localIfAddrs.insert(std::pair<InetAddress,std::string>(ip, ifname));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ip4_address_error:
|
||||||
|
free(configuration.ifc_buf);
|
||||||
|
if (controlfd > 0) close(controlfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool gotViaProc = (localIfAddrs.size() > 0);
|
||||||
|
#else
|
||||||
|
const bool gotViaProc = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!gotViaProc) {
|
||||||
struct ifaddrs *ifatbl = (struct ifaddrs *)0;
|
struct ifaddrs *ifatbl = (struct ifaddrs *)0;
|
||||||
struct ifaddrs *ifa;
|
struct ifaddrs *ifa;
|
||||||
if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
|
if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
|
||||||
@@ -189,7 +308,7 @@ public:
|
|||||||
}
|
}
|
||||||
freeifaddrs(ifatbl);
|
freeifaddrs(ifatbl);
|
||||||
}
|
}
|
||||||
*/
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ struct HttpPhyHandler
|
|||||||
phy->close(sock);
|
phy->close(sock);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void phyOnTcpWritable(PhySocket *sock,void **uptr, bool lwip_invoked)
|
inline void phyOnTcpWritable(PhySocket *sock,void **uptr, bool stack_invoked)
|
||||||
{
|
{
|
||||||
if (writePtr < writeSize) {
|
if (writePtr < writeSize) {
|
||||||
long n = phy->streamSend(sock,writeBuf + writePtr,writeSize - writePtr,true);
|
long n = phy->streamSend(sock,writeBuf + writePtr,writeSize - writePtr,true);
|
||||||
@@ -118,7 +118,7 @@ struct HttpPhyHandler
|
|||||||
inline void phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN) {}
|
inline void phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN) {}
|
||||||
inline void phyOnUnixClose(PhySocket *sock,void **uptr) {}
|
inline void phyOnUnixClose(PhySocket *sock,void **uptr) {}
|
||||||
inline void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) {}
|
inline void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) {}
|
||||||
inline void phyOnUnixWritable(PhySocket *sock,void **uptr, bool lwip_invoked) {}
|
inline void phyOnUnixWritable(PhySocket *sock,void **uptr) {}
|
||||||
#endif // __UNIX_LIKE__
|
#endif // __UNIX_LIKE__
|
||||||
|
|
||||||
http_parser parser;
|
http_parser parser;
|
||||||
|
|||||||
@@ -109,7 +109,12 @@ LinuxEthernetTap::LinuxEthernetTap(
|
|||||||
if (!recalledDevice) {
|
if (!recalledDevice) {
|
||||||
int devno = 0;
|
int devno = 0;
|
||||||
do {
|
do {
|
||||||
|
#ifdef __SYNOLOGY__
|
||||||
|
devno+=50; // Arbitrary number to prevent interface name conflicts
|
||||||
|
Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"eth%d",devno++);
|
||||||
|
#else
|
||||||
Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"zt%d",devno++);
|
Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"zt%d",devno++);
|
||||||
|
#endif
|
||||||
Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
|
Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
|
||||||
} while (stat(procpath,&sbuf) == 0); // try zt#++ until we find one that does not exist
|
} while (stat(procpath,&sbuf) == 0); // try zt#++ until we find one that does not exist
|
||||||
}
|
}
|
||||||
@@ -215,6 +220,57 @@ static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LinuxEthernetTap::addIpSyn(std::vector<InetAddress> ips)
|
||||||
|
{
|
||||||
|
// Here we fill out interface config (ifcfg-dev) to prevent it from being killed
|
||||||
|
std::string filepath = "/etc/sysconfig/network-scripts/ifcfg-"+_dev;
|
||||||
|
std::string cfg_contents = "DEVICE="+_dev+"\nBOOTPROTO=static";
|
||||||
|
int ip4=0,ip6=0,ip4_tot=0,ip6_tot=0;
|
||||||
|
|
||||||
|
long cpid = (long)vfork();
|
||||||
|
if (cpid == 0) {
|
||||||
|
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
|
||||||
|
setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
|
||||||
|
// We must know if there is at least (one) of each protocol version so we
|
||||||
|
// can properly enumerate address/netmask combinations in the ifcfg-dev file
|
||||||
|
for(int i=0; i<ips.size(); i++) {
|
||||||
|
if (ips[i].isV4())
|
||||||
|
ip4_tot++;
|
||||||
|
else
|
||||||
|
ip6_tot++;
|
||||||
|
}
|
||||||
|
// Assemble and write contents of ifcfg-dev file
|
||||||
|
for(int i=0; i<ips.size(); i++) {
|
||||||
|
if (ips[i].isV4()) {
|
||||||
|
std::string numstr4 = ip4_tot > 1 ? std::to_string(ip4) : "";
|
||||||
|
cfg_contents += "\nIPADDR"+numstr4+"="+ips[i].toIpString()
|
||||||
|
+ "\nNETMASK"+numstr4+"="+ips[i].netmask().toIpString()+"\n";
|
||||||
|
ip4++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::string numstr6 = ip6_tot > 1 ? std::to_string(ip6) : "";
|
||||||
|
cfg_contents += "\nIPV6ADDR"+numstr6+"="+ips[i].toIpString()
|
||||||
|
+ "\nNETMASK"+numstr6+"="+ips[i].netmask().toIpString()+"\n";
|
||||||
|
ip6++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OSUtils::writeFile(filepath.c_str(), cfg_contents.c_str(), cfg_contents.length());
|
||||||
|
// Finaly, add IPs
|
||||||
|
for(int i=0; i<ips.size(); i++){
|
||||||
|
if (ips[i].isV4())
|
||||||
|
::execlp("ip","ip","addr","add",ips[i].toString().c_str(),"broadcast",ips[i].broadcast().toIpString().c_str(),"dev",_dev.c_str(),(const char *)0);
|
||||||
|
else
|
||||||
|
::execlp("ip","ip","addr","add",ips[i].toString().c_str(),"dev",_dev.c_str(),(const char *)0);
|
||||||
|
}
|
||||||
|
::_exit(-1);
|
||||||
|
} else if (cpid > 0) {
|
||||||
|
int exitcode = -1;
|
||||||
|
::waitpid(cpid,&exitcode,0);
|
||||||
|
return (exitcode == 0);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool LinuxEthernetTap::addIp(const InetAddress &ip)
|
bool LinuxEthernetTap::addIp(const InetAddress &ip)
|
||||||
{
|
{
|
||||||
if (!ip)
|
if (!ip)
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ public:
|
|||||||
void setEnabled(bool en);
|
void setEnabled(bool en);
|
||||||
bool enabled() const;
|
bool enabled() const;
|
||||||
bool addIp(const InetAddress &ip);
|
bool addIp(const InetAddress &ip);
|
||||||
|
bool addIpSyn(std::vector<InetAddress> ips);
|
||||||
bool removeIp(const InetAddress &ip);
|
bool removeIp(const InetAddress &ip);
|
||||||
std::vector<InetAddress> ips() const;
|
std::vector<InetAddress> ips() const;
|
||||||
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
|
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
|
||||||
|
|||||||
@@ -38,12 +38,8 @@
|
|||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <net/if.h>
|
|
||||||
#ifdef __IOS__
|
|
||||||
#include "/usr/include/net/route.h"
|
|
||||||
#else
|
|
||||||
#include <net/route.h>
|
#include <net/route.h>
|
||||||
#endif
|
#include <net/if.h>
|
||||||
#ifdef __BSD__
|
#ifdef __BSD__
|
||||||
#include <net/if_dl.h>
|
#include <net/if_dl.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
@@ -73,22 +69,27 @@ static void _forkTarget(const InetAddress &t,InetAddress &left,InetAddress &righ
|
|||||||
{
|
{
|
||||||
const unsigned int bits = t.netmaskBits() + 1;
|
const unsigned int bits = t.netmaskBits() + 1;
|
||||||
left = t;
|
left = t;
|
||||||
if ((t.ss_family == AF_INET)&&(bits <= 32)) {
|
if (t.ss_family == AF_INET) {
|
||||||
|
if (bits <= 32) {
|
||||||
left.setPort(bits);
|
left.setPort(bits);
|
||||||
right = t;
|
right = t;
|
||||||
reinterpret_cast<struct sockaddr_in *>(&right)->sin_addr.s_addr ^= Utils::hton((uint32_t)(1 << (32 - bits)));
|
reinterpret_cast<struct sockaddr_in *>(&right)->sin_addr.s_addr ^= Utils::hton((uint32_t)(1 << (32 - bits)));
|
||||||
right.setPort(bits);
|
right.setPort(bits);
|
||||||
} else if ((t.ss_family == AF_INET6)&&(bits <= 128)) {
|
} else {
|
||||||
|
right.zero();
|
||||||
|
}
|
||||||
|
} else if (t.ss_family == AF_INET6) {
|
||||||
|
if (bits <= 128) {
|
||||||
left.setPort(bits);
|
left.setPort(bits);
|
||||||
right = t;
|
right = t;
|
||||||
uint8_t *b = reinterpret_cast<uint8_t *>(reinterpret_cast<struct sockaddr_in6 *>(&right)->sin6_addr.s6_addr);
|
uint8_t *b = reinterpret_cast<uint8_t *>(reinterpret_cast<struct sockaddr_in6 *>(&right)->sin6_addr.s6_addr);
|
||||||
b[bits / 8] ^= 1 << (8 - (bits % 8));
|
b[bits / 8] ^= 1 << (8 - (bits % 8));
|
||||||
right.setPort(bits);
|
right.setPort(bits);
|
||||||
|
} else {
|
||||||
|
right.zero();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __BSD__ // ------------------------------------------------------------
|
|
||||||
#define ZT_ROUTING_SUPPORT_FOUND 1
|
|
||||||
|
|
||||||
struct _RTE
|
struct _RTE
|
||||||
{
|
{
|
||||||
@@ -99,6 +100,9 @@ struct _RTE
|
|||||||
bool ifscope;
|
bool ifscope;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __BSD__ // ------------------------------------------------------------
|
||||||
|
#define ZT_ROUTING_SUPPORT_FOUND 1
|
||||||
|
|
||||||
static std::vector<_RTE> _getRTEs(const InetAddress &target,bool contains)
|
static std::vector<_RTE> _getRTEs(const InetAddress &target,bool contains)
|
||||||
{
|
{
|
||||||
std::vector<_RTE> rtes;
|
std::vector<_RTE> rtes;
|
||||||
@@ -236,6 +240,7 @@ static std::vector<_RTE> _getRTEs(const InetAddress &target,bool contains)
|
|||||||
|
|
||||||
static void _routeCmd(const char *op,const InetAddress &target,const InetAddress &via,const char *ifscope,const char *localInterface)
|
static void _routeCmd(const char *op,const InetAddress &target,const InetAddress &via,const char *ifscope,const char *localInterface)
|
||||||
{
|
{
|
||||||
|
//printf("route %s %s %s %s %s\n",op,target.toString().c_str(),(via) ? via.toString().c_str() : "(null)",(ifscope) ? ifscope : "(null)",(localInterface) ? localInterface : "(null)");
|
||||||
long p = (long)fork();
|
long p = (long)fork();
|
||||||
if (p > 0) {
|
if (p > 0) {
|
||||||
int exitcode = -1;
|
int exitcode = -1;
|
||||||
@@ -373,18 +378,6 @@ bool ManagedRoute::sync()
|
|||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ((_target.isDefaultRoute())||((_target.ss_family == AF_INET)&&(_target.netmaskBits() < 32))) {
|
|
||||||
/* In ZeroTier we create two more specific routes for every one route. We
|
|
||||||
* do this for default routes and IPv4 routes other than /32s. If there
|
|
||||||
* is a pre-existing system route that this route will override, we create
|
|
||||||
* two more specific interface-bound shadow routes for it.
|
|
||||||
*
|
|
||||||
* This means that ZeroTier can *itself* continue communicating over
|
|
||||||
* whatever physical routes might be present while simultaneously
|
|
||||||
* overriding them for general system traffic. This is mostly for
|
|
||||||
* "full tunnel" VPN modes of operation, but might be useful for
|
|
||||||
* virtualizing physical networks in a hybrid design as well. */
|
|
||||||
|
|
||||||
// Generate two more specific routes than target with one extra bit
|
// Generate two more specific routes than target with one extra bit
|
||||||
InetAddress leftt,rightt;
|
InetAddress leftt,rightt;
|
||||||
_forkTarget(_target,leftt,rightt);
|
_forkTarget(_target,leftt,rightt);
|
||||||
@@ -399,119 +392,109 @@ bool ManagedRoute::sync()
|
|||||||
std::vector<_RTE> rtes(_getRTEs(_target,false));
|
std::vector<_RTE> rtes(_getRTEs(_target,false));
|
||||||
for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
|
for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
|
||||||
if (r->via) {
|
if (r->via) {
|
||||||
if ((!newSystemVia)||(r->metric < systemMetric)) {
|
if ( ((!newSystemVia)||(r->metric < systemMetric)) && (strcmp(r->device,_device) != 0) ) {
|
||||||
newSystemVia = r->via;
|
newSystemVia = r->via;
|
||||||
Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
|
Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
|
||||||
systemMetric = r->metric;
|
systemMetric = r->metric;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get device corresponding to route if we don't have that already
|
||||||
if ((newSystemVia)&&(!newSystemDevice[0])) {
|
if ((newSystemVia)&&(!newSystemDevice[0])) {
|
||||||
rtes = _getRTEs(newSystemVia,true);
|
rtes = _getRTEs(newSystemVia,true);
|
||||||
for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
|
for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
|
||||||
if (r->device[0]) {
|
if ( (r->device[0]) && (strcmp(r->device,_device) != 0) ) {
|
||||||
Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
|
Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!newSystemDevice[0])
|
||||||
|
newSystemVia.zero();
|
||||||
|
|
||||||
// Shadow system route if it exists, also delete any obsolete shadows
|
// Shadow system route if it exists, also delete any obsolete shadows
|
||||||
// and replace them with the new state. sync() is called periodically to
|
// and replace them with the new state. sync() is called periodically to
|
||||||
// allow us to do that if underlying connectivity changes.
|
// allow us to do that if underlying connectivity changes.
|
||||||
if ( ((_systemVia != newSystemVia)||(strcmp(_systemDevice,newSystemDevice))) && (strcmp(_device,newSystemDevice)) ) {
|
if ((_systemVia != newSystemVia)||(strcmp(_systemDevice,newSystemDevice) != 0)) {
|
||||||
if ((_systemVia)&&(_systemDevice[0])) {
|
if (_systemVia) {
|
||||||
_routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
|
||||||
|
if (rightt)
|
||||||
_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_systemVia = newSystemVia;
|
_systemVia = newSystemVia;
|
||||||
Utils::scopy(_systemDevice,sizeof(_systemDevice),newSystemDevice);
|
Utils::scopy(_systemDevice,sizeof(_systemDevice),newSystemDevice);
|
||||||
|
|
||||||
if ((_systemVia)&&(_systemDevice[0])) {
|
if (_systemVia) {
|
||||||
_routeCmd("add",leftt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("add",leftt,_systemVia,_systemDevice,(const char *)0);
|
||||||
_routeCmd("change",leftt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("change",leftt,_systemVia,_systemDevice,(const char *)0);
|
||||||
|
if (rightt) {
|
||||||
_routeCmd("add",rightt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("add",rightt,_systemVia,_systemDevice,(const char *)0);
|
||||||
_routeCmd("change",rightt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("change",rightt,_systemVia,_systemDevice,(const char *)0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply overriding non-device-scoped routes
|
|
||||||
if (!_applied) {
|
|
||||||
if (_via) {
|
|
||||||
_routeCmd("add",leftt,_via,(const char *)0,(const char *)0);
|
|
||||||
_routeCmd("change",leftt,_via,(const char *)0,(const char *)0);
|
|
||||||
_routeCmd("add",rightt,_via,(const char *)0,(const char *)0);
|
|
||||||
_routeCmd("change",rightt,_via,(const char *)0,(const char *)0);
|
|
||||||
} else if (_device[0]) {
|
|
||||||
_routeCmd("add",leftt,_via,(const char *)0,_device);
|
|
||||||
_routeCmd("change",leftt,_via,(const char *)0,_device);
|
|
||||||
_routeCmd("add",rightt,_via,(const char *)0,_device);
|
|
||||||
_routeCmd("change",rightt,_via,(const char *)0,_device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_applied = true;
|
if (!_applied.count(leftt)) {
|
||||||
|
_applied[leftt] = false; // not ifscoped
|
||||||
|
_routeCmd("add",leftt,_via,(const char *)0,(_via) ? (const char *)0 : _device);
|
||||||
|
_routeCmd("change",leftt,_via,(const char *)0,(_via) ? (const char *)0 : _device);
|
||||||
|
}
|
||||||
|
if ((rightt)&&(!_applied.count(rightt))) {
|
||||||
|
_applied[rightt] = false; // not ifscoped
|
||||||
|
_routeCmd("add",rightt,_via,(const char *)0,(_via) ? (const char *)0 : _device);
|
||||||
|
_routeCmd("change",rightt,_via,(const char *)0,(_via) ? (const char *)0 : _device);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __BSD__ ------------------------------------------------------------
|
// Create a device-bound default target if there is none in the system. This
|
||||||
|
// is to allow e.g. IPv6 default route to work even if there is no native
|
||||||
#ifdef __LINUX__ // ----------------------------------------------------------
|
// IPv6 on your LAN.
|
||||||
|
/*
|
||||||
if (!_applied) {
|
if (_target.isDefaultRoute()) {
|
||||||
_routeCmd("replace",leftt,_via,(_via) ? _device : (const char *)0);
|
if (_systemVia) {
|
||||||
_routeCmd("replace",rightt,_via,(_via) ? _device : (const char *)0);
|
if (_applied.count(_target)) {
|
||||||
_applied = true;
|
_applied.erase(_target);
|
||||||
|
_routeCmd("delete",_target,_via,_device,(_via) ? (const char *)0 : _device);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __LINUX__ ----------------------------------------------------------
|
|
||||||
|
|
||||||
#ifdef __WINDOWS__ // --------------------------------------------------------
|
|
||||||
|
|
||||||
if (!_applied) {
|
|
||||||
_winRoute(false,interfaceLuid,interfaceIndex,leftt,_via);
|
|
||||||
_winRoute(false,interfaceLuid,interfaceIndex,rightt,_via);
|
|
||||||
_applied = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __WINDOWS__ --------------------------------------------------------
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if (!_applied.count(_target)) {
|
||||||
#ifdef __BSD__ // ------------------------------------------------------------
|
_applied[_target] = true; // ifscoped
|
||||||
|
_routeCmd("add",_target,_via,_device,(_via) ? (const char *)0 : _device);
|
||||||
if (!_applied) {
|
_routeCmd("change",_target,_via,_device,(_via) ? (const char *)0 : _device);
|
||||||
if (_via) {
|
|
||||||
_routeCmd("add",_target,_via,(const char *)0,(const char *)0);
|
|
||||||
_routeCmd("change",_target,_via,(const char *)0,(const char *)0);
|
|
||||||
} else if (_device[0]) {
|
|
||||||
_routeCmd("add",_target,_via,(const char *)0,_device);
|
|
||||||
_routeCmd("change",_target,_via,(const char *)0,_device);
|
|
||||||
}
|
}
|
||||||
_applied = true;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#endif // __BSD__ ------------------------------------------------------------
|
#endif // __BSD__ ------------------------------------------------------------
|
||||||
|
|
||||||
#ifdef __LINUX__ // ----------------------------------------------------------
|
#ifdef __LINUX__ // ----------------------------------------------------------
|
||||||
|
|
||||||
if (!_applied) {
|
if (!_applied.count(leftt)) {
|
||||||
_routeCmd("replace",_target,_via,(_via) ? _device : (const char *)0);
|
_applied[leftt] = false; // boolean unused
|
||||||
_applied = true;
|
_routeCmd("replace",leftt,_via,(_via) ? (const char *)0 : _device);
|
||||||
|
}
|
||||||
|
if ((rightt)&&(!_applied.count(rightt))) {
|
||||||
|
_applied[rightt] = false; // boolean unused
|
||||||
|
_routeCmd("replace",rightt,_via,(_via) ? (const char *)0 : _device);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __LINUX__ ----------------------------------------------------------
|
#endif // __LINUX__ ----------------------------------------------------------
|
||||||
|
|
||||||
#ifdef __WINDOWS__ // --------------------------------------------------------
|
#ifdef __WINDOWS__ // --------------------------------------------------------
|
||||||
|
|
||||||
if (!_applied) {
|
if (!_applied.count(leftt)) {
|
||||||
_winRoute(false,interfaceLuid,interfaceIndex,_target,_via);
|
_applied[leftt] = false; // boolean unused
|
||||||
_applied = true;
|
_winRoute(false,interfaceLuid,interfaceIndex,leftt,_via);
|
||||||
|
}
|
||||||
|
if ((rightt)&&(!_applied.count(rightt))) {
|
||||||
|
_applied[rightt] = false; // boolean unused
|
||||||
|
_winRoute(false,interfaceLuid,interfaceIndex,rightt,_via);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __WINDOWS__ --------------------------------------------------------
|
#endif // __WINDOWS__ --------------------------------------------------------
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -525,66 +508,28 @@ void ManagedRoute::remove()
|
|||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (_applied) {
|
#ifdef __BSD__
|
||||||
if ((_target.isDefaultRoute())||((_target.ss_family == AF_INET)&&(_target.netmaskBits() < 32))) {
|
if (_systemVia) {
|
||||||
InetAddress leftt,rightt;
|
InetAddress leftt,rightt;
|
||||||
_forkTarget(_target,leftt,rightt);
|
_forkTarget(_target,leftt,rightt);
|
||||||
|
|
||||||
#ifdef __BSD__ // ------------------------------------------------------------
|
|
||||||
|
|
||||||
if ((_systemVia)&&(_systemDevice[0])) {
|
|
||||||
_routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
|
||||||
|
if (rightt)
|
||||||
_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
|
||||||
}
|
}
|
||||||
if (_via) {
|
|
||||||
_routeCmd("delete",leftt,_via,(const char *)0,(const char *)0);
|
|
||||||
_routeCmd("delete",rightt,_via,(const char *)0,(const char *)0);
|
|
||||||
} else if (_device[0]) {
|
|
||||||
_routeCmd("delete",leftt,_via,(const char *)0,_device);
|
|
||||||
_routeCmd("delete",rightt,_via,(const char *)0,_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __BSD__ ------------------------------------------------------------
|
#endif // __BSD__ ------------------------------------------------------------
|
||||||
|
|
||||||
#ifdef __LINUX__ // ----------------------------------------------------------
|
for(std::map<InetAddress,bool>::iterator r(_applied.begin());r!=_applied.end();++r) {
|
||||||
|
|
||||||
_routeCmd("del",leftt,_via,(_via) ? _device : (const char *)0);
|
|
||||||
_routeCmd("del",rightt,_via,(_via) ? _device : (const char *)0);
|
|
||||||
|
|
||||||
#endif // __LINUX__ ----------------------------------------------------------
|
|
||||||
|
|
||||||
#ifdef __WINDOWS__ // --------------------------------------------------------
|
|
||||||
|
|
||||||
_winRoute(true,interfaceLuid,interfaceIndex,leftt,_via);
|
|
||||||
_winRoute(true,interfaceLuid,interfaceIndex,rightt,_via);
|
|
||||||
|
|
||||||
#endif // __WINDOWS__ --------------------------------------------------------
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
#ifdef __BSD__ // ------------------------------------------------------------
|
#ifdef __BSD__ // ------------------------------------------------------------
|
||||||
|
_routeCmd("delete",r->first,_via,r->second ? _device : (const char *)0,(_via) ? (const char *)0 : _device);
|
||||||
if (_via) {
|
|
||||||
_routeCmd("delete",_target,_via,(const char *)0,(const char *)0);
|
|
||||||
} else if (_device[0]) {
|
|
||||||
_routeCmd("delete",_target,_via,(const char *)0,_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __BSD__ ------------------------------------------------------------
|
#endif // __BSD__ ------------------------------------------------------------
|
||||||
|
|
||||||
#ifdef __LINUX__ // ----------------------------------------------------------
|
#ifdef __LINUX__ // ----------------------------------------------------------
|
||||||
|
_routeCmd("del",r->first,_via,(_via) ? (const char *)0 : _device);
|
||||||
_routeCmd("del",_target,_via,(_via) ? _device : (const char *)0);
|
|
||||||
|
|
||||||
#endif // __LINUX__ ----------------------------------------------------------
|
#endif // __LINUX__ ----------------------------------------------------------
|
||||||
|
|
||||||
#ifdef __WINDOWS__ // --------------------------------------------------------
|
#ifdef __WINDOWS__ // --------------------------------------------------------
|
||||||
|
_winRoute(true,interfaceLuid,interfaceIndex,r->first,_via);
|
||||||
_winRoute(true,interfaceLuid,interfaceIndex,_target,_via);
|
|
||||||
|
|
||||||
#endif // __WINDOWS__ --------------------------------------------------------
|
#endif // __WINDOWS__ --------------------------------------------------------
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_target.zero();
|
_target.zero();
|
||||||
@@ -592,7 +537,7 @@ void ManagedRoute::remove()
|
|||||||
_systemVia.zero();
|
_systemVia.zero();
|
||||||
_device[0] = (char)0;
|
_device[0] = (char)0;
|
||||||
_systemDevice[0] = (char)0;
|
_systemDevice[0] = (char)0;
|
||||||
_applied = false;
|
_applied.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -6,23 +6,34 @@
|
|||||||
|
|
||||||
#include "../node/InetAddress.hpp"
|
#include "../node/InetAddress.hpp"
|
||||||
#include "../node/Utils.hpp"
|
#include "../node/Utils.hpp"
|
||||||
|
#include "../node/SharedPtr.hpp"
|
||||||
|
#include "../node/AtomicCounter.hpp"
|
||||||
|
#include "../node/NonCopyable.hpp"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ZT-managed route that used C++ RAII semantics to automatically clean itself up on deallocate
|
* A ZT-managed route that used C++ RAII semantics to automatically clean itself up on deallocate
|
||||||
*/
|
*/
|
||||||
class ManagedRoute
|
class ManagedRoute : NonCopyable
|
||||||
{
|
{
|
||||||
|
friend class SharedPtr<ManagedRoute>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ManagedRoute()
|
ManagedRoute(const InetAddress &target,const InetAddress &via,const char *device)
|
||||||
{
|
{
|
||||||
_device[0] = (char)0;
|
_target = target;
|
||||||
|
_via = via;
|
||||||
|
if (via.ss_family == AF_INET)
|
||||||
|
_via.setPort(32);
|
||||||
|
else if (via.ss_family == AF_INET6)
|
||||||
|
_via.setPort(128);
|
||||||
|
Utils::scopy(_device,sizeof(_device),device);
|
||||||
_systemDevice[0] = (char)0;
|
_systemDevice[0] = (char)0;
|
||||||
_applied = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~ManagedRoute()
|
~ManagedRoute()
|
||||||
@@ -30,45 +41,6 @@ public:
|
|||||||
this->remove();
|
this->remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
ManagedRoute(const ManagedRoute &r)
|
|
||||||
{
|
|
||||||
_applied = false;
|
|
||||||
*this = r;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ManagedRoute &operator=(const ManagedRoute &r)
|
|
||||||
{
|
|
||||||
if ((!_applied)&&(!r._applied)) {
|
|
||||||
memcpy(this,&r,sizeof(ManagedRoute)); // InetAddress is memcpy'able
|
|
||||||
} else {
|
|
||||||
fprintf(stderr,"Applied ManagedRoute isn't copyable!\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize object and set route
|
|
||||||
*
|
|
||||||
* Note: on Windows, use the interface NET_LUID in hexadecimal as the
|
|
||||||
* "device name."
|
|
||||||
*
|
|
||||||
* @param target Route target (e.g. 0.0.0.0/0 for default)
|
|
||||||
* @param via Route next L3 hop or NULL InetAddress if local in which case it will be routed via device
|
|
||||||
* @param device Name or hex LUID of ZeroTier device (e.g. zt#)
|
|
||||||
* @return True if route was successfully set
|
|
||||||
*/
|
|
||||||
inline bool set(const InetAddress &target,const InetAddress &via,const char *device)
|
|
||||||
{
|
|
||||||
if ((!via)&&(!device[0]))
|
|
||||||
return false;
|
|
||||||
this->remove();
|
|
||||||
_target = target;
|
|
||||||
_via = via;
|
|
||||||
Utils::scopy(_device,sizeof(_device),device);
|
|
||||||
return this->sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set or update currently set route
|
* Set or update currently set route
|
||||||
*
|
*
|
||||||
@@ -93,13 +65,14 @@ public:
|
|||||||
inline const char *device() const { return _device; }
|
inline const char *device() const { return _device; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
InetAddress _target;
|
InetAddress _target;
|
||||||
InetAddress _via;
|
InetAddress _via;
|
||||||
InetAddress _systemVia; // for route overrides
|
InetAddress _systemVia; // for route overrides
|
||||||
|
std::map<InetAddress,bool> _applied; // routes currently applied
|
||||||
char _device[128];
|
char _device[128];
|
||||||
char _systemDevice[128]; // for route overrides
|
char _systemDevice[128]; // for route overrides
|
||||||
bool _applied;
|
|
||||||
|
AtomicCounter __refCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "../node/Constants.hpp"
|
#include "../node/Constants.hpp"
|
||||||
|
#include "../node/Utils.hpp"
|
||||||
|
|
||||||
#ifdef __UNIX_LIKE__
|
#ifdef __UNIX_LIKE__
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -107,6 +108,90 @@ std::vector<std::string> OSUtils::listDirectory(const char *path)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::map<std::string,char> OSUtils::listDirectoryFull(const char *path)
|
||||||
|
{
|
||||||
|
std::map<std::string,char> r;
|
||||||
|
|
||||||
|
#ifdef __WINDOWS__
|
||||||
|
HANDLE hFind;
|
||||||
|
WIN32_FIND_DATAA ffd;
|
||||||
|
if ((hFind = FindFirstFileA((std::string(path) + "\\*").c_str(),&ffd)) != INVALID_HANDLE_VALUE) {
|
||||||
|
do {
|
||||||
|
if ((strcmp(ffd.cFileName,"."))&&(strcmp(ffd.cFileName,".."))) {
|
||||||
|
r[ffd.cFileName] = ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) ? 'd' : 'f';
|
||||||
|
}
|
||||||
|
} while (FindNextFileA(hFind,&ffd));
|
||||||
|
FindClose(hFind);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
struct dirent de;
|
||||||
|
struct dirent *dptr;
|
||||||
|
DIR *d = opendir(path);
|
||||||
|
if (!d)
|
||||||
|
return r;
|
||||||
|
dptr = (struct dirent *)0;
|
||||||
|
for(;;) {
|
||||||
|
if (readdir_r(d,&de,&dptr))
|
||||||
|
break;
|
||||||
|
if (dptr) {
|
||||||
|
if ((strcmp(dptr->d_name,"."))&&(strcmp(dptr->d_name,".."))) {
|
||||||
|
r[dptr->d_name] = (dptr->d_type == DT_DIR) ? 'd' : 'f';
|
||||||
|
}
|
||||||
|
} else break;
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OSUtils::rmDashRf(const char *path)
|
||||||
|
{
|
||||||
|
#ifdef __WINDOWS__
|
||||||
|
HANDLE hFind;
|
||||||
|
WIN32_FIND_DATAA ffd;
|
||||||
|
if ((hFind = FindFirstFileA((std::string(path) + "\\*").c_str(),&ffd)) != INVALID_HANDLE_VALUE) {
|
||||||
|
do {
|
||||||
|
if ((strcmp(ffd.cFileName,".") != 0)&&(strcmp(ffd.cFileName,"..") != 0)) {
|
||||||
|
if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||||
|
if (DeleteFileA((std::string(path) + ZT_PATH_SEPARATOR_S + ffd.cFileName).c_str()) == FALSE)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (!rmDashRf((std::string(path) + ZT_PATH_SEPARATOR_S + ffd.cFileName).c_str()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (FindNextFileA(hFind,&ffd));
|
||||||
|
FindClose(hFind);
|
||||||
|
}
|
||||||
|
return (RemoveDirectoryA(path) != FALSE);
|
||||||
|
#else
|
||||||
|
struct dirent de;
|
||||||
|
struct dirent *dptr;
|
||||||
|
DIR *d = opendir(path);
|
||||||
|
if (!d)
|
||||||
|
return true;
|
||||||
|
dptr = (struct dirent *)0;
|
||||||
|
for(;;) {
|
||||||
|
if (readdir_r(d,&de,&dptr) != 0)
|
||||||
|
break;
|
||||||
|
if (!dptr)
|
||||||
|
break;
|
||||||
|
if ((strcmp(dptr->d_name,".") != 0)&&(strcmp(dptr->d_name,"..") != 0)&&(strlen(dptr->d_name) > 0)) {
|
||||||
|
std::string p(path);
|
||||||
|
p.push_back(ZT_PATH_SEPARATOR);
|
||||||
|
p.append(dptr->d_name);
|
||||||
|
if (unlink(p.c_str()) != 0) { // unlink first will remove symlinks instead of recursing them
|
||||||
|
if (!rmDashRf(p.c_str()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
return (rmdir(path) == 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void OSUtils::lockDownFile(const char *path,bool isDir)
|
void OSUtils::lockDownFile(const char *path,bool isDir)
|
||||||
{
|
{
|
||||||
#ifdef __UNIX_LIKE__
|
#ifdef __UNIX_LIKE__
|
||||||
@@ -171,36 +256,6 @@ int64_t OSUtils::getFileSize(const char *path)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<InetAddress> OSUtils::resolve(const char *name)
|
|
||||||
{
|
|
||||||
std::vector<InetAddress> r;
|
|
||||||
std::vector<InetAddress>::iterator i;
|
|
||||||
InetAddress tmp;
|
|
||||||
struct addrinfo *ai = (struct addrinfo *)0,*p;
|
|
||||||
/*
|
|
||||||
if (!getaddrinfo(name,(const char *)0,(const struct addrinfo *)0,&ai)) {
|
|
||||||
try {
|
|
||||||
p = ai;
|
|
||||||
while (p) {
|
|
||||||
if ((p->ai_addr)&&((p->ai_addr->sa_family == AF_INET)||(p->ai_addr->sa_family == AF_INET6))) {
|
|
||||||
tmp = *(p->ai_addr);
|
|
||||||
for(i=r.begin();i!=r.end();++i) {
|
|
||||||
if (i->ipsEqual(tmp))
|
|
||||||
goto skip_add_inetaddr;
|
|
||||||
}
|
|
||||||
r.push_back(tmp);
|
|
||||||
}
|
|
||||||
skip_add_inetaddr:
|
|
||||||
p = p->ai_next;
|
|
||||||
}
|
|
||||||
} catch ( ... ) {}
|
|
||||||
freeaddrinfo(ai);
|
|
||||||
}
|
|
||||||
std::sort(r.begin(),r.end());
|
|
||||||
*/
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OSUtils::readFile(const char *path,std::string &buf)
|
bool OSUtils::readFile(const char *path,std::string &buf)
|
||||||
{
|
{
|
||||||
char tmp[1024];
|
char tmp[1024];
|
||||||
@@ -233,6 +288,50 @@ bool OSUtils::writeFile(const char *path,const void *buf,unsigned int len)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> OSUtils::split(const char *s,const char *const sep,const char *esc,const char *quot)
|
||||||
|
{
|
||||||
|
std::vector<std::string> fields;
|
||||||
|
std::string buf;
|
||||||
|
|
||||||
|
if (!esc)
|
||||||
|
esc = "";
|
||||||
|
if (!quot)
|
||||||
|
quot = "";
|
||||||
|
|
||||||
|
bool escapeState = false;
|
||||||
|
char quoteState = 0;
|
||||||
|
while (*s) {
|
||||||
|
if (escapeState) {
|
||||||
|
escapeState = false;
|
||||||
|
buf.push_back(*s);
|
||||||
|
} else if (quoteState) {
|
||||||
|
if (*s == quoteState) {
|
||||||
|
quoteState = 0;
|
||||||
|
fields.push_back(buf);
|
||||||
|
buf.clear();
|
||||||
|
} else buf.push_back(*s);
|
||||||
|
} else {
|
||||||
|
const char *quotTmp;
|
||||||
|
if (strchr(esc,*s))
|
||||||
|
escapeState = true;
|
||||||
|
else if ((buf.size() <= 0)&&((quotTmp = strchr(quot,*s))))
|
||||||
|
quoteState = *quotTmp;
|
||||||
|
else if (strchr(sep,*s)) {
|
||||||
|
if (buf.size() > 0) {
|
||||||
|
fields.push_back(buf);
|
||||||
|
buf.clear();
|
||||||
|
} // else skip runs of seperators
|
||||||
|
} else buf.push_back(*s);
|
||||||
|
}
|
||||||
|
++s;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf.size())
|
||||||
|
fields.push_back(buf);
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
std::string OSUtils::platformDefaultHomePath()
|
std::string OSUtils::platformDefaultHomePath()
|
||||||
{
|
{
|
||||||
#ifdef __UNIX_LIKE__
|
#ifdef __UNIX_LIKE__
|
||||||
@@ -269,6 +368,81 @@ std::string OSUtils::platformDefaultHomePath()
|
|||||||
#endif // __UNIX_LIKE__ or not...
|
#endif // __UNIX_LIKE__ or not...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inline these massive JSON operations in one place only to reduce binary footprint and compile time
|
||||||
|
nlohmann::json OSUtils::jsonParse(const std::string &buf) { return nlohmann::json::parse(buf); }
|
||||||
|
std::string OSUtils::jsonDump(const nlohmann::json &j) { return j.dump(1); }
|
||||||
|
|
||||||
|
uint64_t OSUtils::jsonInt(const nlohmann::json &jv,const uint64_t dfl)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (jv.is_number()) {
|
||||||
|
return (uint64_t)jv;
|
||||||
|
} else if (jv.is_string()) {
|
||||||
|
std::string s = jv;
|
||||||
|
return Utils::strToU64(s.c_str());
|
||||||
|
} else if (jv.is_boolean()) {
|
||||||
|
return ((bool)jv ? 1ULL : 0ULL);
|
||||||
|
}
|
||||||
|
} catch ( ... ) {}
|
||||||
|
return dfl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OSUtils::jsonBool(const nlohmann::json &jv,const bool dfl)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (jv.is_boolean()) {
|
||||||
|
return (bool)jv;
|
||||||
|
} else if (jv.is_number()) {
|
||||||
|
return ((uint64_t)jv > 0ULL);
|
||||||
|
} else if (jv.is_string()) {
|
||||||
|
std::string s = jv;
|
||||||
|
if (s.length() > 0) {
|
||||||
|
switch(s[0]) {
|
||||||
|
case 't':
|
||||||
|
case 'T':
|
||||||
|
case '1':
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch ( ... ) {}
|
||||||
|
return dfl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OSUtils::jsonString(const nlohmann::json &jv,const char *dfl)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (jv.is_string()) {
|
||||||
|
return jv;
|
||||||
|
} else if (jv.is_number()) {
|
||||||
|
char tmp[64];
|
||||||
|
Utils::snprintf(tmp,sizeof(tmp),"%llu",(uint64_t)jv);
|
||||||
|
return tmp;
|
||||||
|
} else if (jv.is_boolean()) {
|
||||||
|
return ((bool)jv ? std::string("1") : std::string("0"));
|
||||||
|
}
|
||||||
|
} catch ( ... ) {}
|
||||||
|
return std::string((dfl) ? dfl : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OSUtils::jsonBinFromHex(const nlohmann::json &jv)
|
||||||
|
{
|
||||||
|
std::string s(jsonString(jv,""));
|
||||||
|
if (s.length() > 0) {
|
||||||
|
char *buf = new char[(s.length() / 2) + 1];
|
||||||
|
try {
|
||||||
|
unsigned int l = Utils::unhex(s,buf,(unsigned int)s.length());
|
||||||
|
std::string b(buf,l);
|
||||||
|
delete [] buf;
|
||||||
|
return b;
|
||||||
|
} catch ( ... ) {
|
||||||
|
delete [] buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
// Used to convert HTTP header names to ASCII lower case
|
// Used to convert HTTP header names to ASCII lower case
|
||||||
const unsigned char OSUtils::TOLOWER_TABLE[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
|
const unsigned char OSUtils::TOLOWER_TABLE[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,8 @@
|
|||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "../ext/json/json.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,10 +107,26 @@ public:
|
|||||||
* This returns only files, not sub-directories.
|
* This returns only files, not sub-directories.
|
||||||
*
|
*
|
||||||
* @param path Path to list
|
* @param path Path to list
|
||||||
* @return Names of files in directory
|
* @return Names of files in directory (without path prepended)
|
||||||
*/
|
*/
|
||||||
static std::vector<std::string> listDirectory(const char *path);
|
static std::vector<std::string> listDirectory(const char *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all contents in a directory
|
||||||
|
*
|
||||||
|
* @param path Path to list
|
||||||
|
* @return Names of things and types, currently just 'f' and 'd'
|
||||||
|
*/
|
||||||
|
static std::map<std::string,char> listDirectoryFull(const char *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a directory and all its files and subdirectories recursively
|
||||||
|
*
|
||||||
|
* @param path Path to delete
|
||||||
|
* @return True on success
|
||||||
|
*/
|
||||||
|
static bool rmDashRf(const char *path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set modes on a file to something secure
|
* Set modes on a file to something secure
|
||||||
*
|
*
|
||||||
@@ -220,6 +238,17 @@ public:
|
|||||||
*/
|
*/
|
||||||
static bool writeFile(const char *path,const void *buf,unsigned int len);
|
static bool writeFile(const char *path,const void *buf,unsigned int len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split a string by delimiter, with optional escape and quote characters
|
||||||
|
*
|
||||||
|
* @param s String to split
|
||||||
|
* @param sep One or more separators
|
||||||
|
* @param esc Zero or more escape characters
|
||||||
|
* @param quot Zero or more quote characters
|
||||||
|
* @return Vector of tokens
|
||||||
|
*/
|
||||||
|
static std::vector<std::string> split(const char *s,const char *const sep,const char *esc,const char *quot);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a block of data to disk, replacing any current file contents
|
* Write a block of data to disk, replacing any current file contents
|
||||||
*
|
*
|
||||||
@@ -240,6 +269,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
static std::string platformDefaultHomePath();
|
static std::string platformDefaultHomePath();
|
||||||
|
|
||||||
|
static nlohmann::json jsonParse(const std::string &buf);
|
||||||
|
static std::string jsonDump(const nlohmann::json &j);
|
||||||
|
static uint64_t jsonInt(const nlohmann::json &jv,const uint64_t dfl);
|
||||||
|
static bool jsonBool(const nlohmann::json &jv,const bool dfl);
|
||||||
|
static std::string jsonString(const nlohmann::json &jv,const char *dfl);
|
||||||
|
static std::string jsonBinFromHex(const nlohmann::json &jv);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const unsigned char TOLOWER_TABLE[256];
|
static const unsigned char TOLOWER_TABLE[256];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include <WinSock2.h>
|
#include <WinSock2.h>
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "../node/Mutex.hpp"
|
#include "../node/Mutex.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
@@ -126,12 +127,17 @@ public:
|
|||||||
{
|
{
|
||||||
memset(&_tid,0,sizeof(_tid));
|
memset(&_tid,0,sizeof(_tid));
|
||||||
pthread_attr_init(&_tattr);
|
pthread_attr_init(&_tattr);
|
||||||
#ifdef __LINUX__
|
// This corrects for systems with abnormally small defaults (musl) and also
|
||||||
pthread_attr_setstacksize(&_tattr,8388608); // for MUSL libc and others, has no effect in normal glibc environments
|
// shrinks the stack on systems with large defaults to save a bit of memory.
|
||||||
#endif
|
pthread_attr_setstacksize(&_tattr,ZT_THREAD_MIN_STACK_SIZE);
|
||||||
_started = false;
|
_started = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~Thread()
|
||||||
|
{
|
||||||
|
pthread_attr_destroy(&_tattr);
|
||||||
|
}
|
||||||
|
|
||||||
Thread(const Thread &t)
|
Thread(const Thread &t)
|
||||||
throw()
|
throw()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -599,10 +599,10 @@ WindowsEthernetTap::WindowsEthernetTap(
|
|||||||
unsigned int tmpsl = Utils::snprintf(tmps,sizeof(tmps),"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",(unsigned int)mac[0],(unsigned int)mac[1],(unsigned int)mac[2],(unsigned int)mac[3],(unsigned int)mac[4],(unsigned int)mac[5]) + 1;
|
unsigned int tmpsl = Utils::snprintf(tmps,sizeof(tmps),"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",(unsigned int)mac[0],(unsigned int)mac[1],(unsigned int)mac[2],(unsigned int)mac[3],(unsigned int)mac[4],(unsigned int)mac[5]) + 1;
|
||||||
RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"NetworkAddress",REG_SZ,tmps,tmpsl);
|
RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"NetworkAddress",REG_SZ,tmps,tmpsl);
|
||||||
RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MAC",REG_SZ,tmps,tmpsl);
|
RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MAC",REG_SZ,tmps,tmpsl);
|
||||||
DWORD tmp = mtu;
|
tmpsl = Utils::snprintf(tmps, sizeof(tmps), "%d", mtu);
|
||||||
RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MTU",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
|
RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MTU",REG_SZ,tmps,tmpsl);
|
||||||
|
|
||||||
tmp = 0;
|
DWORD tmp = 0;
|
||||||
RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"*NdisDeviceType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
|
RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"*NdisDeviceType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
|
||||||
tmp = IF_TYPE_ETHERNET_CSMACD;
|
tmp = IF_TYPE_ETHERNET_CSMACD;
|
||||||
RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"*IfType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
|
RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"*IfType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
|
||||||
@@ -640,7 +640,7 @@ WindowsEthernetTap::WindowsEthernetTap(
|
|||||||
if (ConvertInterfaceGuidToLuid(&_deviceGuid,&_deviceLuid) != NO_ERROR)
|
if (ConvertInterfaceGuidToLuid(&_deviceGuid,&_deviceLuid) != NO_ERROR)
|
||||||
throw std::runtime_error("unable to convert device interface GUID to LUID");
|
throw std::runtime_error("unable to convert device interface GUID to LUID");
|
||||||
|
|
||||||
_initialized = true;
|
//_initialized = true;
|
||||||
|
|
||||||
if (friendlyName)
|
if (friendlyName)
|
||||||
setFriendlyName(friendlyName);
|
setFriendlyName(friendlyName);
|
||||||
@@ -672,6 +672,7 @@ bool WindowsEthernetTap::addIp(const InetAddress &ip)
|
|||||||
{
|
{
|
||||||
if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT?
|
if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT?
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Mutex::Lock _l(_assignedIps_m);
|
Mutex::Lock _l(_assignedIps_m);
|
||||||
if (std::find(_assignedIps.begin(),_assignedIps.end(),ip) != _assignedIps.end())
|
if (std::find(_assignedIps.begin(),_assignedIps.end(),ip) != _assignedIps.end())
|
||||||
return true;
|
return true;
|
||||||
@@ -682,6 +683,9 @@ bool WindowsEthernetTap::addIp(const InetAddress &ip)
|
|||||||
|
|
||||||
bool WindowsEthernetTap::removeIp(const InetAddress &ip)
|
bool WindowsEthernetTap::removeIp(const InetAddress &ip)
|
||||||
{
|
{
|
||||||
|
if (ip.isV6())
|
||||||
|
return true;
|
||||||
|
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_assignedIps_m);
|
Mutex::Lock _l(_assignedIps_m);
|
||||||
std::vector<InetAddress>::iterator aip(std::find(_assignedIps.begin(),_assignedIps.end(),ip));
|
std::vector<InetAddress>::iterator aip(std::find(_assignedIps.begin(),_assignedIps.end(),ip));
|
||||||
@@ -713,6 +717,7 @@ bool WindowsEthernetTap::removeIp(const InetAddress &ip)
|
|||||||
DeleteUnicastIpAddressEntry(&(ipt->Table[i]));
|
DeleteUnicastIpAddressEntry(&(ipt->Table[i]));
|
||||||
FreeMibTable(ipt);
|
FreeMibTable(ipt);
|
||||||
|
|
||||||
|
if (ip.isV4()) {
|
||||||
std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
|
std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
|
||||||
std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
|
std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
|
||||||
std::string ipstr(ip.toIpString());
|
std::string ipstr(ip.toIpString());
|
||||||
@@ -724,6 +729,7 @@ bool WindowsEthernetTap::removeIp(const InetAddress &ip)
|
|||||||
_setRegistryIPv4Value("SubnetMask", regSubnetMasks);
|
_setRegistryIPv4Value("SubnetMask", regSubnetMasks);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -1001,6 +1007,10 @@ void WindowsEthernetTap::threadMain()
|
|||||||
ReadFile(_tap,tapReadBuf,sizeof(tapReadBuf),NULL,&tapOvlRead);
|
ReadFile(_tap,tapReadBuf,sizeof(tapReadBuf),NULL,&tapOvlRead);
|
||||||
bool writeInProgress = false;
|
bool writeInProgress = false;
|
||||||
ULONGLONG timeOfLastBorkCheck = GetTickCount64();
|
ULONGLONG timeOfLastBorkCheck = GetTickCount64();
|
||||||
|
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
|
|
||||||
while (_run) {
|
while (_run) {
|
||||||
DWORD waitResult = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,2500,TRUE);
|
DWORD waitResult = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,2500,TRUE);
|
||||||
if (!_run) break; // will also break outer while(_run)
|
if (!_run) break; // will also break outer while(_run)
|
||||||
@@ -1195,7 +1205,9 @@ void WindowsEthernetTap::_syncIps()
|
|||||||
CreateUnicastIpAddressEntry(&ipr);
|
CreateUnicastIpAddressEntry(&ipr);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ipStr(aip->toString());
|
if (aip->isV4())
|
||||||
|
{
|
||||||
|
std::string ipStr(aip->toIpString());
|
||||||
std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
|
std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
|
||||||
if (std::find(regIps.begin(), regIps.end(), ipStr) == regIps.end()) {
|
if (std::find(regIps.begin(), regIps.end(), ipStr) == regIps.end()) {
|
||||||
std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
|
std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
|
||||||
@@ -1206,5 +1218,6 @@ void WindowsEthernetTap::_syncIps()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ public:
|
|||||||
void threadMain()
|
void threadMain()
|
||||||
throw();
|
throw();
|
||||||
|
|
||||||
|
bool isInitialized() const { return _initialized; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NET_IFINDEX _getDeviceIndex(); // throws on failure
|
NET_IFINDEX _getDeviceIndex(); // throws on failure
|
||||||
std::vector<std::string> _getRegistryIPv4Value(const char *regKey);
|
std::vector<std::string> _getRegistryIPv4Value(const char *regKey);
|
||||||
|
|||||||
@@ -49,13 +49,10 @@
|
|||||||
#include "osdep/OSUtils.hpp"
|
#include "osdep/OSUtils.hpp"
|
||||||
#include "osdep/Phy.hpp"
|
#include "osdep/Phy.hpp"
|
||||||
#include "osdep/Http.hpp"
|
#include "osdep/Http.hpp"
|
||||||
#include "osdep/BackgroundResolver.hpp"
|
|
||||||
#include "osdep/PortMapper.hpp"
|
#include "osdep/PortMapper.hpp"
|
||||||
#include "osdep/Thread.hpp"
|
#include "osdep/Thread.hpp"
|
||||||
|
|
||||||
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
|
#include "controller/JSONDB.hpp"
|
||||||
#include "controller/SqliteNetworkController.hpp"
|
|
||||||
#endif // ZT_ENABLE_NETWORK_CONTROLLER
|
|
||||||
|
|
||||||
#ifdef __WINDOWS__
|
#ifdef __WINDOWS__
|
||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
@@ -157,9 +154,9 @@ static int testCrypto()
|
|||||||
memset(buf3,0,sizeof(buf3));
|
memset(buf3,0,sizeof(buf3));
|
||||||
Salsa20 s20;
|
Salsa20 s20;
|
||||||
s20.init("12345678123456781234567812345678",256,"12345678");
|
s20.init("12345678123456781234567812345678",256,"12345678");
|
||||||
s20.encrypt20(buf1,buf2,sizeof(buf1));
|
s20.crypt20(buf1,buf2,sizeof(buf1));
|
||||||
s20.init("12345678123456781234567812345678",256,"12345678");
|
s20.init("12345678123456781234567812345678",256,"12345678");
|
||||||
s20.decrypt20(buf2,buf3,sizeof(buf2));
|
s20.crypt20(buf2,buf3,sizeof(buf2));
|
||||||
if (memcmp(buf1,buf3,sizeof(buf1))) {
|
if (memcmp(buf1,buf3,sizeof(buf1))) {
|
||||||
std::cout << "FAIL (encrypt/decrypt test)" << std::endl;
|
std::cout << "FAIL (encrypt/decrypt test)" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -168,7 +165,7 @@ static int testCrypto()
|
|||||||
Salsa20 s20(s20TV0Key,256,s20TV0Iv);
|
Salsa20 s20(s20TV0Key,256,s20TV0Iv);
|
||||||
memset(buf1,0,sizeof(buf1));
|
memset(buf1,0,sizeof(buf1));
|
||||||
memset(buf2,0,sizeof(buf2));
|
memset(buf2,0,sizeof(buf2));
|
||||||
s20.encrypt20(buf1,buf2,64);
|
s20.crypt20(buf1,buf2,64);
|
||||||
if (memcmp(buf2,s20TV0Ks,64)) {
|
if (memcmp(buf2,s20TV0Ks,64)) {
|
||||||
std::cout << "FAIL (test vector 0)" << std::endl;
|
std::cout << "FAIL (test vector 0)" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -176,7 +173,7 @@ static int testCrypto()
|
|||||||
s20.init(s2012TV0Key,256,s2012TV0Iv);
|
s20.init(s2012TV0Key,256,s2012TV0Iv);
|
||||||
memset(buf1,0,sizeof(buf1));
|
memset(buf1,0,sizeof(buf1));
|
||||||
memset(buf2,0,sizeof(buf2));
|
memset(buf2,0,sizeof(buf2));
|
||||||
s20.encrypt12(buf1,buf2,64);
|
s20.crypt12(buf1,buf2,64);
|
||||||
if (memcmp(buf2,s2012TV0Ks,64)) {
|
if (memcmp(buf2,s2012TV0Ks,64)) {
|
||||||
std::cout << "FAIL (test vector 1)" << std::endl;
|
std::cout << "FAIL (test vector 1)" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -198,7 +195,7 @@ static int testCrypto()
|
|||||||
double bytes = 0.0;
|
double bytes = 0.0;
|
||||||
uint64_t start = OSUtils::now();
|
uint64_t start = OSUtils::now();
|
||||||
for(unsigned int i=0;i<200;++i) {
|
for(unsigned int i=0;i<200;++i) {
|
||||||
s20.encrypt12(bb,bb,1234567);
|
s20.crypt12(bb,bb,1234567);
|
||||||
bytes += 1234567.0;
|
bytes += 1234567.0;
|
||||||
}
|
}
|
||||||
uint64_t end = OSUtils::now();
|
uint64_t end = OSUtils::now();
|
||||||
@@ -216,7 +213,7 @@ static int testCrypto()
|
|||||||
double bytes = 0.0;
|
double bytes = 0.0;
|
||||||
uint64_t start = OSUtils::now();
|
uint64_t start = OSUtils::now();
|
||||||
for(unsigned int i=0;i<200;++i) {
|
for(unsigned int i=0;i<200;++i) {
|
||||||
s20.encrypt20(bb,bb,1234567);
|
s20.crypt20(bb,bb,1234567);
|
||||||
bytes += 1234567.0;
|
bytes += 1234567.0;
|
||||||
}
|
}
|
||||||
uint64_t end = OSUtils::now();
|
uint64_t end = OSUtils::now();
|
||||||
@@ -329,6 +326,17 @@ static int testCrypto()
|
|||||||
}
|
}
|
||||||
std::cout << "PASS" << std::endl;
|
std::cout << "PASS" << std::endl;
|
||||||
|
|
||||||
|
std::cout << "[crypto] Benchmarking C25519 ECC key agreement... "; std::cout.flush();
|
||||||
|
C25519::Pair bp[8];
|
||||||
|
for(int k=0;k<8;++k)
|
||||||
|
bp[k] = C25519::generate();
|
||||||
|
const uint64_t st = OSUtils::now();
|
||||||
|
for(unsigned int k=0;k<50;++k) {
|
||||||
|
C25519::agree(bp[~k & 7],bp[k & 7].pub,buf1,64);
|
||||||
|
}
|
||||||
|
const uint64_t et = OSUtils::now();
|
||||||
|
std::cout << ((double)(et - st) / 50.0) << "ms per agreement." << std::endl;
|
||||||
|
|
||||||
std::cout << "[crypto] Testing Ed25519 ECC signatures... "; std::cout.flush();
|
std::cout << "[crypto] Testing Ed25519 ECC signatures... "; std::cout.flush();
|
||||||
C25519::Pair didntSign = C25519::generate();
|
C25519::Pair didntSign = C25519::generate();
|
||||||
for(unsigned int i=0;i<10;++i) {
|
for(unsigned int i=0;i<10;++i) {
|
||||||
@@ -378,11 +386,15 @@ static int testIdentity()
|
|||||||
std::cout << "FAIL (1)" << std::endl;
|
std::cout << "FAIL (1)" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
const uint64_t vst = OSUtils::now();
|
||||||
|
for(int k=0;k<10;++k) {
|
||||||
if (!id.locallyValidate()) {
|
if (!id.locallyValidate()) {
|
||||||
std::cout << "FAIL (2)" << std::endl;
|
std::cout << "FAIL (2)" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
std::cout << "PASS" << std::endl;
|
}
|
||||||
|
const uint64_t vet = OSUtils::now();
|
||||||
|
std::cout << "PASS (" << ((double)(vet - vst) / 10.0) << "ms per validation)" << std::endl;
|
||||||
|
|
||||||
std::cout << "[identity] Validate known-bad identity... "; std::cout.flush();
|
std::cout << "[identity] Validate known-bad identity... "; std::cout.flush();
|
||||||
if (!id.fromString(KNOWN_BAD_IDENTITY)) {
|
if (!id.fromString(KNOWN_BAD_IDENTITY)) {
|
||||||
@@ -505,19 +517,6 @@ static int testCertificate()
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "[certificate] Testing string serialization... ";
|
|
||||||
CertificateOfMembership copyA(cA.toString());
|
|
||||||
CertificateOfMembership copyB(cB.toString());
|
|
||||||
if (copyA != cA) {
|
|
||||||
std::cout << "FAIL" << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (copyB != cB) {
|
|
||||||
std::cout << "FAIL" << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
std::cout << "PASS" << std::endl;
|
|
||||||
|
|
||||||
std::cout << "[certificate] Generating two certificates that should not agree...";
|
std::cout << "[certificate] Generating two certificates that should not agree...";
|
||||||
cA = CertificateOfMembership(10000,100,1,idA.address());
|
cA = CertificateOfMembership(10000,100,1,idA.address());
|
||||||
cB = CertificateOfMembership(10101,100,1,idB.address());
|
cB = CertificateOfMembership(10101,100,1,idB.address());
|
||||||
@@ -573,7 +572,7 @@ static int testPacket()
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.armor(salsaKey,true);
|
a.armor(salsaKey,true,0);
|
||||||
if (!a.dearmor(salsaKey)) {
|
if (!a.dearmor(salsaKey)) {
|
||||||
std::cout << "FAIL (encrypt-decrypt/verify)" << std::endl;
|
std::cout << "FAIL (encrypt-decrypt/verify)" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -824,6 +823,43 @@ static int testOther()
|
|||||||
}
|
}
|
||||||
std::cout << "PASS (junk value to prevent optimization-out of test: " << foo << ")" << std::endl;
|
std::cout << "PASS (junk value to prevent optimization-out of test: " << foo << ")" << std::endl;
|
||||||
|
|
||||||
|
/*
|
||||||
|
std::cout << "[other] Testing controller/JSONDB..."; std::cout.flush();
|
||||||
|
{
|
||||||
|
std::map<std::string,nlohmann::json> db1data;
|
||||||
|
JSONDB db1("jsondb-test");
|
||||||
|
for(unsigned int i=0;i<256;++i) {
|
||||||
|
std::string n;
|
||||||
|
for(unsigned int j=0,k=rand() % 4;j<=k;++j) {
|
||||||
|
if (j > 0) n.push_back('/');
|
||||||
|
char foo[24];
|
||||||
|
Utils::snprintf(foo,sizeof(foo),"%lx",rand());
|
||||||
|
n.append(foo);
|
||||||
|
}
|
||||||
|
db1data[n] = {{"i",i}};
|
||||||
|
db1.put(n,db1data[n]);
|
||||||
|
}
|
||||||
|
for(std::map<std::string,nlohmann::json>::iterator i(db1data.begin());i!=db1data.end();++i) {
|
||||||
|
i->second["foo"] = "bar";
|
||||||
|
db1.put(i->first,i->second);
|
||||||
|
}
|
||||||
|
JSONDB db2("jsondb-test");
|
||||||
|
if (db1 != db2) {
|
||||||
|
std::cout << " FAILED (db1!=db2 #1)" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for(std::map<std::string,nlohmann::json>::iterator i(db1data.begin());i!=db1data.end();++i) {
|
||||||
|
db1.erase(i->first);
|
||||||
|
}
|
||||||
|
db2.reload();
|
||||||
|
if (db1 != db2) {
|
||||||
|
std::cout << " FAILED (db1!=db2 #2)" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << " PASS" << std::endl;
|
||||||
|
*/
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -975,23 +1011,6 @@ static int testPhy()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int testResolver()
|
|
||||||
{
|
|
||||||
std::cout << "[resolver] Testing BackgroundResolver..."; std::cout.flush();
|
|
||||||
|
|
||||||
BackgroundResolver r("tcp-fallback.zerotier.com");
|
|
||||||
r.resolveNow();
|
|
||||||
r.wait();
|
|
||||||
|
|
||||||
std::vector<InetAddress> ips(r.get());
|
|
||||||
for(std::vector<InetAddress>::const_iterator ip(ips.begin());ip!=ips.end();++ip) {
|
|
||||||
std::cout << ' ' << ip->toString();
|
|
||||||
}
|
|
||||||
std::cout << std::endl;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
static int testHttp()
|
static int testHttp()
|
||||||
{
|
{
|
||||||
@@ -1099,7 +1118,6 @@ int main(int argc,char **argv)
|
|||||||
r |= testIdentity();
|
r |= testIdentity();
|
||||||
r |= testCertificate();
|
r |= testCertificate();
|
||||||
r |= testPhy();
|
r |= testPhy();
|
||||||
r |= testResolver();
|
|
||||||
//r |= testHttp();
|
//r |= testHttp();
|
||||||
//*/
|
//*/
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ public:
|
|||||||
char myAddressStr[64];
|
char myAddressStr[64];
|
||||||
Utils::snprintf(myAddressStr,sizeof(myAddressStr),"%.10llx",myAddress);
|
Utils::snprintf(myAddressStr,sizeof(myAddressStr),"%.10llx",myAddress);
|
||||||
|
|
||||||
std::vector<std::string> lines(Utils::split(cf.c_str(),"\r\n","",""));
|
std::vector<std::string> lines(OSUtils::split(cf.c_str(),"\r\n","",""));
|
||||||
for(std::vector<std::string>::iterator l(lines.begin());l!=lines.end();++l) {
|
for(std::vector<std::string>::iterator l(lines.begin());l!=lines.end();++l) {
|
||||||
std::vector<std::string> fields(Utils::split(l->c_str()," \t","",""));
|
std::vector<std::string> fields(OSUtils::split(l->c_str()," \t","",""));
|
||||||
if ((fields.size() < 5)||(fields[0][0] == '#')||(fields[0] != myAddressStr))
|
if ((fields.size() < 5)||(fields[0][0] == '#')||(fields[0] != myAddressStr))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ public:
|
|||||||
|
|
||||||
md.id = (unsigned int)id;
|
md.id = (unsigned int)id;
|
||||||
if (fields.size() >= 6) {
|
if (fields.size() >= 6) {
|
||||||
std::vector<std::string> xyz(Utils::split(fields[5].c_str(),",","",""));
|
std::vector<std::string> xyz(OSUtils::split(fields[5].c_str(),",","",""));
|
||||||
md.x = (xyz.size() > 0) ? Utils::strToInt(xyz[0].c_str()) : 0;
|
md.x = (xyz.size() > 0) ? Utils::strToInt(xyz[0].c_str()) : 0;
|
||||||
md.y = (xyz.size() > 1) ? Utils::strToInt(xyz[1].c_str()) : 0;
|
md.y = (xyz.size() > 1) ? Utils::strToInt(xyz[1].c_str()) : 0;
|
||||||
md.z = (xyz.size() > 2) ? Utils::strToInt(xyz[2].c_str()) : 0;
|
md.z = (xyz.size() > 2) ? Utils::strToInt(xyz[2].c_str()) : 0;
|
||||||
@@ -102,7 +102,7 @@ public:
|
|||||||
md.clusterEndpoint.fromString(fields[3]);
|
md.clusterEndpoint.fromString(fields[3]);
|
||||||
if (!md.clusterEndpoint)
|
if (!md.clusterEndpoint)
|
||||||
continue;
|
continue;
|
||||||
std::vector<std::string> zips(Utils::split(fields[4].c_str(),",","",""));
|
std::vector<std::string> zips(OSUtils::split(fields[4].c_str(),",","",""));
|
||||||
for(std::vector<std::string>::iterator zip(zips.begin());zip!=zips.end();++zip) {
|
for(std::vector<std::string>::iterator zip(zips.begin());zip!=zips.end();++zip) {
|
||||||
InetAddress i;
|
InetAddress i;
|
||||||
i.fromString(*zip);
|
i.fromString(*zip);
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ bool ClusterGeoIpService::locate(const InetAddress &ip,int &x,int &y,int &z)
|
|||||||
|
|
||||||
void ClusterGeoIpService::_parseLine(const char *line,std::vector<_V4E> &v4db,std::vector<_V6E> &v6db,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
|
void ClusterGeoIpService::_parseLine(const char *line,std::vector<_V4E> &v4db,std::vector<_V6E> &v6db,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
|
||||||
{
|
{
|
||||||
std::vector<std::string> ls(Utils::split(line,",\t","\\","\"'"));
|
std::vector<std::string> ls(OSUtils::split(line,",\t","\\","\"'"));
|
||||||
if ( ((ipStartColumn >= 0)&&(ipStartColumn < (int)ls.size()))&&
|
if ( ((ipStartColumn >= 0)&&(ipStartColumn < (int)ls.size()))&&
|
||||||
((ipEndColumn >= 0)&&(ipEndColumn < (int)ls.size()))&&
|
((ipEndColumn >= 0)&&(ipEndColumn < (int)ls.size()))&&
|
||||||
((latitudeColumn >= 0)&&(latitudeColumn < (int)ls.size()))&&
|
((latitudeColumn >= 0)&&(latitudeColumn < (int)ls.size()))&&
|
||||||
|
|||||||
@@ -28,90 +28,24 @@
|
|||||||
#include "../ext/http-parser/http_parser.h"
|
#include "../ext/http-parser/http_parser.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ZT_USE_SYSTEM_JSON_PARSER
|
#include "../ext/json/json.hpp"
|
||||||
#include <json-parser/json.h>
|
|
||||||
#else
|
|
||||||
#include "../ext/json-parser/json.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
|
#include "../controller/EmbeddedNetworkController.hpp"
|
||||||
#include "../controller/SqliteNetworkController.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "../node/InetAddress.hpp"
|
#include "../node/InetAddress.hpp"
|
||||||
#include "../node/Node.hpp"
|
#include "../node/Node.hpp"
|
||||||
#include "../node/Utils.hpp"
|
#include "../node/Utils.hpp"
|
||||||
|
#include "../node/World.hpp"
|
||||||
|
|
||||||
#include "../osdep/OSUtils.hpp"
|
#include "../osdep/OSUtils.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
static std::string _jsonEscape(const char *s)
|
namespace {
|
||||||
{
|
|
||||||
std::string buf;
|
|
||||||
for(const char *p=s;(*p);++p) {
|
|
||||||
switch(*p) {
|
|
||||||
case '\t': buf.append("\\t"); break;
|
|
||||||
case '\b': buf.append("\\b"); break;
|
|
||||||
case '\r': buf.append("\\r"); break;
|
|
||||||
case '\n': buf.append("\\n"); break;
|
|
||||||
case '\f': buf.append("\\f"); break;
|
|
||||||
case '"': buf.append("\\\""); break;
|
|
||||||
case '\\': buf.append("\\\\"); break;
|
|
||||||
case '/': buf.append("\\/"); break;
|
|
||||||
default: buf.push_back(*p); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
static std::string _jsonEscape(const std::string &s) { return _jsonEscape(s.c_str()); }
|
|
||||||
|
|
||||||
static std::string _jsonEnumerate(const struct sockaddr_storage *ss,unsigned int count)
|
static void _networkToJson(nlohmann::json &nj,const ZT_VirtualNetworkConfig *nc,const std::string &portDeviceName,const OneService::NetworkSettings &localSettings)
|
||||||
{
|
{
|
||||||
std::string buf;
|
char tmp[256];
|
||||||
buf.push_back('[');
|
|
||||||
for(unsigned int i=0;i<count;++i) {
|
|
||||||
if (i > 0)
|
|
||||||
buf.push_back(',');
|
|
||||||
buf.push_back('"');
|
|
||||||
buf.append(_jsonEscape(reinterpret_cast<const InetAddress *>(&(ss[i]))->toString()));
|
|
||||||
buf.push_back('"');
|
|
||||||
}
|
|
||||||
buf.push_back(']');
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
static std::string _jsonEnumerate(const ZT_VirtualNetworkRoute *routes,unsigned int count)
|
|
||||||
{
|
|
||||||
std::string buf;
|
|
||||||
buf.push_back('[');
|
|
||||||
for(unsigned int i=0;i<count;++i) {
|
|
||||||
if (i > 0)
|
|
||||||
buf.push_back(',');
|
|
||||||
buf.append("{\"target\":\"");
|
|
||||||
buf.append(_jsonEscape(reinterpret_cast<const InetAddress *>(&(routes[i].target))->toString()));
|
|
||||||
buf.append("\",\"via\":");
|
|
||||||
if (routes[i].via.ss_family == routes[i].target.ss_family) {
|
|
||||||
buf.push_back('"');
|
|
||||||
buf.append(_jsonEscape(reinterpret_cast<const InetAddress *>(&(routes[i].via))->toIpString()));
|
|
||||||
buf.append("\",");
|
|
||||||
} else buf.append("null,");
|
|
||||||
char tmp[1024];
|
|
||||||
Utils::snprintf(tmp,sizeof(tmp),"\"flags\":%u,\"metric\":%u}",(unsigned int)routes[i].flags,(unsigned int)routes[i].metric);
|
|
||||||
buf.append(tmp);
|
|
||||||
}
|
|
||||||
buf.push_back(']');
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_VirtualNetworkConfig *nc,const std::string &portDeviceName,const OneService::NetworkSettings &localSettings)
|
|
||||||
{
|
|
||||||
char json[4096];
|
|
||||||
char prefix[32];
|
|
||||||
|
|
||||||
if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
|
|
||||||
return;
|
|
||||||
for(unsigned int i=0;i<depth;++i)
|
|
||||||
prefix[i] = '\t';
|
|
||||||
prefix[depth] = '\0';
|
|
||||||
|
|
||||||
const char *nstatus = "",*ntype = "";
|
const char *nstatus = "",*ntype = "";
|
||||||
switch(nc->status) {
|
switch(nc->status) {
|
||||||
@@ -127,141 +61,110 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_VirtualNetw
|
|||||||
case ZT_NETWORK_TYPE_PUBLIC: ntype = "PUBLIC"; break;
|
case ZT_NETWORK_TYPE_PUBLIC: ntype = "PUBLIC"; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::snprintf(json,sizeof(json),
|
Utils::snprintf(tmp,sizeof(tmp),"%.16llx",nc->nwid);
|
||||||
"%s{\n"
|
nj["id"] = tmp;
|
||||||
"%s\t\"nwid\": \"%.16llx\",\n"
|
nj["nwid"] = tmp;
|
||||||
"%s\t\"mac\": \"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\",\n"
|
Utils::snprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)((nc->mac >> 40) & 0xff),(unsigned int)((nc->mac >> 32) & 0xff),(unsigned int)((nc->mac >> 24) & 0xff),(unsigned int)((nc->mac >> 16) & 0xff),(unsigned int)((nc->mac >> 8) & 0xff),(unsigned int)(nc->mac & 0xff));
|
||||||
"%s\t\"name\": \"%s\",\n"
|
nj["mac"] = tmp;
|
||||||
"%s\t\"status\": \"%s\",\n"
|
nj["name"] = nc->name;
|
||||||
"%s\t\"type\": \"%s\",\n"
|
nj["status"] = nstatus;
|
||||||
"%s\t\"mtu\": %u,\n"
|
nj["type"] = ntype;
|
||||||
"%s\t\"dhcp\": %s,\n"
|
nj["mtu"] = nc->mtu;
|
||||||
"%s\t\"bridge\": %s,\n"
|
nj["dhcp"] = (bool)(nc->dhcp != 0);
|
||||||
"%s\t\"broadcastEnabled\": %s,\n"
|
nj["bridge"] = (bool)(nc->bridge != 0);
|
||||||
"%s\t\"portError\": %d,\n"
|
nj["broadcastEnabled"] = (bool)(nc->broadcastEnabled != 0);
|
||||||
"%s\t\"netconfRevision\": %lu,\n"
|
nj["portError"] = nc->portError;
|
||||||
"%s\t\"assignedAddresses\": %s,\n"
|
nj["netconfRevision"] = nc->netconfRevision;
|
||||||
"%s\t\"routes\": %s,\n"
|
nj["portDeviceName"] = portDeviceName;
|
||||||
"%s\t\"portDeviceName\": \"%s\",\n"
|
nj["allowManaged"] = localSettings.allowManaged;
|
||||||
"%s\t\"allowManaged\": %s,\n"
|
nj["allowGlobal"] = localSettings.allowGlobal;
|
||||||
"%s\t\"allowGlobal\": %s,\n"
|
nj["allowDefault"] = localSettings.allowDefault;
|
||||||
"%s\t\"allowDefault\": %s\n"
|
|
||||||
"%s}",
|
nlohmann::json aa = nlohmann::json::array();
|
||||||
prefix,
|
for(unsigned int i=0;i<nc->assignedAddressCount;++i) {
|
||||||
prefix,nc->nwid,
|
aa.push_back(reinterpret_cast<const InetAddress *>(&(nc->assignedAddresses[i]))->toString());
|
||||||
prefix,(unsigned int)((nc->mac >> 40) & 0xff),(unsigned int)((nc->mac >> 32) & 0xff),(unsigned int)((nc->mac >> 24) & 0xff),(unsigned int)((nc->mac >> 16) & 0xff),(unsigned int)((nc->mac >> 8) & 0xff),(unsigned int)(nc->mac & 0xff),
|
}
|
||||||
prefix,_jsonEscape(nc->name).c_str(),
|
nj["assignedAddresses"] = aa;
|
||||||
prefix,nstatus,
|
|
||||||
prefix,ntype,
|
nlohmann::json ra = nlohmann::json::array();
|
||||||
prefix,nc->mtu,
|
for(unsigned int i=0;i<nc->routeCount;++i) {
|
||||||
prefix,(nc->dhcp == 0) ? "false" : "true",
|
nlohmann::json rj;
|
||||||
prefix,(nc->bridge == 0) ? "false" : "true",
|
rj["target"] = reinterpret_cast<const InetAddress *>(&(nc->routes[i].target))->toString();
|
||||||
prefix,(nc->broadcastEnabled == 0) ? "false" : "true",
|
if (nc->routes[i].via.ss_family == nc->routes[i].target.ss_family)
|
||||||
prefix,nc->portError,
|
rj["via"] = reinterpret_cast<const InetAddress *>(&(nc->routes[i].via))->toIpString();
|
||||||
prefix,nc->netconfRevision,
|
else rj["via"] = nlohmann::json();
|
||||||
prefix,_jsonEnumerate(nc->assignedAddresses,nc->assignedAddressCount).c_str(),
|
rj["flags"] = (int)nc->routes[i].flags;
|
||||||
prefix,_jsonEnumerate(nc->routes,nc->routeCount).c_str(),
|
rj["metric"] = (int)nc->routes[i].metric;
|
||||||
prefix,_jsonEscape(portDeviceName).c_str(),
|
ra.push_back(rj);
|
||||||
prefix,(localSettings.allowManaged) ? "true" : "false",
|
}
|
||||||
prefix,(localSettings.allowGlobal) ? "true" : "false",
|
nj["routes"] = ra;
|
||||||
prefix,(localSettings.allowDefault) ? "true" : "false",
|
|
||||||
prefix);
|
|
||||||
buf.append(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string _jsonEnumerate(unsigned int depth,const ZT_PeerPhysicalPath *pp,unsigned int count)
|
static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer)
|
||||||
{
|
{
|
||||||
char json[1024];
|
char tmp[256];
|
||||||
char prefix[32];
|
|
||||||
|
|
||||||
if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
|
|
||||||
return std::string();
|
|
||||||
for(unsigned int i=0;i<depth;++i)
|
|
||||||
prefix[i] = '\t';
|
|
||||||
prefix[depth] = '\0';
|
|
||||||
|
|
||||||
std::string buf;
|
|
||||||
for(unsigned int i=0;i<count;++i) {
|
|
||||||
if (i > 0)
|
|
||||||
buf.push_back(',');
|
|
||||||
Utils::snprintf(json,sizeof(json),
|
|
||||||
"{\n"
|
|
||||||
"%s\t\"address\": \"%s\",\n"
|
|
||||||
"%s\t\"lastSend\": %llu,\n"
|
|
||||||
"%s\t\"lastReceive\": %llu,\n"
|
|
||||||
"%s\t\"active\": %s,\n"
|
|
||||||
"%s\t\"preferred\": %s,\n"
|
|
||||||
"%s\t\"trustedPathId\": %llu\n"
|
|
||||||
"%s}",
|
|
||||||
prefix,_jsonEscape(reinterpret_cast<const InetAddress *>(&(pp[i].address))->toString()).c_str(),
|
|
||||||
prefix,pp[i].lastSend,
|
|
||||||
prefix,pp[i].lastReceive,
|
|
||||||
prefix,(pp[i].active == 0) ? "false" : "true",
|
|
||||||
prefix,(pp[i].preferred == 0) ? "false" : "true",
|
|
||||||
prefix,pp[i].trustedPathId,
|
|
||||||
prefix);
|
|
||||||
buf.append(json);
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer)
|
|
||||||
{
|
|
||||||
char json[1024];
|
|
||||||
char prefix[32];
|
|
||||||
|
|
||||||
if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
|
|
||||||
return;
|
|
||||||
for(unsigned int i=0;i<depth;++i)
|
|
||||||
prefix[i] = '\t';
|
|
||||||
prefix[depth] = '\0';
|
|
||||||
|
|
||||||
const char *prole = "";
|
const char *prole = "";
|
||||||
switch(peer->role) {
|
switch(peer->role) {
|
||||||
case ZT_PEER_ROLE_LEAF: prole = "LEAF"; break;
|
case ZT_PEER_ROLE_LEAF: prole = "LEAF"; break;
|
||||||
case ZT_PEER_ROLE_RELAY: prole = "RELAY"; break;
|
case ZT_PEER_ROLE_MOON: prole = "MOON"; break;
|
||||||
case ZT_PEER_ROLE_ROOT: prole = "ROOT"; break;
|
case ZT_PEER_ROLE_PLANET: prole = "PLANET"; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::snprintf(json,sizeof(json),
|
Utils::snprintf(tmp,sizeof(tmp),"%.10llx",peer->address);
|
||||||
"%s{\n"
|
pj["address"] = tmp;
|
||||||
"%s\t\"address\": \"%.10llx\",\n"
|
pj["versionMajor"] = peer->versionMajor;
|
||||||
"%s\t\"lastUnicastFrame\": %llu,\n"
|
pj["versionMinor"] = peer->versionMinor;
|
||||||
"%s\t\"lastMulticastFrame\": %llu,\n"
|
pj["versionRev"] = peer->versionRev;
|
||||||
"%s\t\"versionMajor\": %d,\n"
|
Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d",peer->versionMajor,peer->versionMinor,peer->versionRev);
|
||||||
"%s\t\"versionMinor\": %d,\n"
|
pj["version"] = tmp;
|
||||||
"%s\t\"versionRev\": %d,\n"
|
pj["latency"] = peer->latency;
|
||||||
"%s\t\"version\": \"%d.%d.%d\",\n"
|
pj["role"] = prole;
|
||||||
"%s\t\"latency\": %u,\n"
|
|
||||||
"%s\t\"role\": \"%s\",\n"
|
nlohmann::json pa = nlohmann::json::array();
|
||||||
"%s\t\"paths\": [%s]\n"
|
for(unsigned int i=0;i<peer->pathCount;++i) {
|
||||||
"%s}",
|
nlohmann::json j;
|
||||||
prefix,
|
j["address"] = reinterpret_cast<const InetAddress *>(&(peer->paths[i].address))->toString();
|
||||||
prefix,peer->address,
|
j["lastSend"] = peer->paths[i].lastSend;
|
||||||
prefix,peer->lastUnicastFrame,
|
j["lastReceive"] = peer->paths[i].lastReceive;
|
||||||
prefix,peer->lastMulticastFrame,
|
j["trustedPathId"] = peer->paths[i].trustedPathId;
|
||||||
prefix,peer->versionMajor,
|
j["linkQuality"] = (double)peer->paths[i].linkQuality / (double)ZT_PATH_LINK_QUALITY_MAX;
|
||||||
prefix,peer->versionMinor,
|
j["active"] = (bool)(peer->paths[i].expired == 0);
|
||||||
prefix,peer->versionRev,
|
j["expired"] = (bool)(peer->paths[i].expired != 0);
|
||||||
prefix,peer->versionMajor,peer->versionMinor,peer->versionRev,
|
j["preferred"] = (bool)(peer->paths[i].preferred != 0);
|
||||||
prefix,peer->latency,
|
pa.push_back(j);
|
||||||
prefix,prole,
|
}
|
||||||
prefix,_jsonEnumerate(depth+1,peer->paths,peer->pathCount).c_str(),
|
pj["paths"] = pa;
|
||||||
prefix);
|
|
||||||
buf.append(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlPlane::ControlPlane(OneService *svc,Node *n,const char *uiStaticPath) :
|
static void _moonToJson(nlohmann::json &mj,const World &world)
|
||||||
|
{
|
||||||
|
char tmp[64];
|
||||||
|
Utils::snprintf(tmp,sizeof(tmp),"%.16llx",world.id());
|
||||||
|
mj["id"] = tmp;
|
||||||
|
mj["timestamp"] = world.timestamp();
|
||||||
|
mj["signature"] = Utils::hex(world.signature().data,(unsigned int)world.signature().size());
|
||||||
|
mj["updatesMustBeSignedBy"] = Utils::hex(world.updatesMustBeSignedBy().data,(unsigned int)world.updatesMustBeSignedBy().size());
|
||||||
|
nlohmann::json ra = nlohmann::json::array();
|
||||||
|
for(std::vector<World::Root>::const_iterator r(world.roots().begin());r!=world.roots().end();++r) {
|
||||||
|
nlohmann::json rj;
|
||||||
|
rj["identity"] = r->identity.toString(false);
|
||||||
|
nlohmann::json eps = nlohmann::json::array();
|
||||||
|
for(std::vector<InetAddress>::const_iterator a(r->stableEndpoints.begin());a!=r->stableEndpoints.end();++a)
|
||||||
|
eps.push_back(a->toString());
|
||||||
|
rj["stableEndpoints"] = eps;
|
||||||
|
ra.push_back(rj);
|
||||||
|
}
|
||||||
|
mj["roots"] = ra;
|
||||||
|
mj["waiting"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
ControlPlane::ControlPlane(OneService *svc,Node *n) :
|
||||||
_svc(svc),
|
_svc(svc),
|
||||||
_node(n),
|
_node(n),
|
||||||
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
|
_controller((EmbeddedNetworkController *)0)
|
||||||
_controller((SqliteNetworkController *)0),
|
|
||||||
#endif
|
|
||||||
_uiStaticPath((uiStaticPath) ? uiStaticPath : "")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ControlPlane::~ControlPlane()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,14 +177,13 @@ unsigned int ControlPlane::handleRequest(
|
|||||||
std::string &responseBody,
|
std::string &responseBody,
|
||||||
std::string &responseContentType)
|
std::string &responseContentType)
|
||||||
{
|
{
|
||||||
char json[8194];
|
char tmp[256];
|
||||||
unsigned int scode = 404;
|
unsigned int scode = 404;
|
||||||
std::vector<std::string> ps(Utils::split(path.c_str(),"/","",""));
|
nlohmann::json res;
|
||||||
|
std::vector<std::string> ps(OSUtils::split(path.c_str(),"/","",""));
|
||||||
std::map<std::string,std::string> urlArgs;
|
std::map<std::string,std::string> urlArgs;
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
|
|
||||||
if (!((fromAddress.ipsEqual(InetAddress::LO4))||(fromAddress.ipsEqual(InetAddress::LO6))))
|
Mutex::Lock _l(_lock);
|
||||||
return 403; // Forbidden: we only allow access from localhost right now
|
|
||||||
|
|
||||||
/* Note: this is kind of restricted in what it'll take. It does not support
|
/* Note: this is kind of restricted in what it'll take. It does not support
|
||||||
* URL encoding, and /'s in URL args will screw it up. But the only URL args
|
* URL encoding, and /'s in URL args will screw it up. But the only URL args
|
||||||
@@ -292,7 +194,7 @@ unsigned int ControlPlane::handleRequest(
|
|||||||
if (qpos != std::string::npos) {
|
if (qpos != std::string::npos) {
|
||||||
std::string args(ps[ps.size() - 1].substr(qpos + 1));
|
std::string args(ps[ps.size() - 1].substr(qpos + 1));
|
||||||
ps[ps.size() - 1] = ps[ps.size() - 1].substr(0,qpos);
|
ps[ps.size() - 1] = ps[ps.size() - 1].substr(0,qpos);
|
||||||
std::vector<std::string> asplit(Utils::split(args.c_str(),"&","",""));
|
std::vector<std::string> asplit(OSUtils::split(args.c_str(),"&","",""));
|
||||||
for(std::vector<std::string>::iterator a(asplit.begin());a!=asplit.end();++a) {
|
for(std::vector<std::string>::iterator a(asplit.begin());a!=asplit.end();++a) {
|
||||||
std::size_t eqpos = a->find('=');
|
std::size_t eqpos = a->find('=');
|
||||||
if (eqpos == std::string::npos)
|
if (eqpos == std::string::npos)
|
||||||
@@ -300,8 +202,6 @@ unsigned int ControlPlane::handleRequest(
|
|||||||
else urlArgs[a->substr(0,eqpos)] = a->substr(eqpos + 1);
|
else urlArgs[a->substr(0,eqpos)] = a->substr(eqpos + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ps.push_back(std::string("index.html"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isAuth = false;
|
bool isAuth = false;
|
||||||
@@ -316,149 +216,163 @@ unsigned int ControlPlane::handleRequest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (httpMethod == HTTP_GET) {
|
#ifdef __SYNOLOGY__
|
||||||
|
#include <stdlib.h>
|
||||||
std::string ext;
|
// Authenticate via Synology's built-in cgi script
|
||||||
std::size_t dotIdx = ps[0].find_last_of('.');
|
if (!isAuth) {
|
||||||
if (dotIdx != std::string::npos)
|
/*
|
||||||
ext = ps[0].substr(dotIdx);
|
fprintf(stderr, "path = %s\n", path.c_str());
|
||||||
|
fprintf(stderr, "headers.size=%d\n", headers.size());
|
||||||
if ((ps.size() == 1)&&(ext.length() >= 2)&&(ext[0] == '.')) {
|
std::map<std::string, std::string>::const_iterator it(headers.begin());
|
||||||
/* Static web pages can be served without authentication to enable a simple web
|
while(it != headers.end()) {
|
||||||
* UI. This is still only allowed from approved IP addresses. Anything with a
|
fprintf(stderr,"header[%s] = %s\n", (it->first).c_str(), (it->second).c_str());
|
||||||
* dot in the first path element (e.g. foo.html) is considered a static page,
|
it++;
|
||||||
* as nothing in the API is so named. */
|
|
||||||
|
|
||||||
if (_uiStaticPath.length() > 0) {
|
|
||||||
if (ext == ".html")
|
|
||||||
responseContentType = "text/html";
|
|
||||||
else if (ext == ".js")
|
|
||||||
responseContentType = "application/javascript";
|
|
||||||
else if (ext == ".jsx")
|
|
||||||
responseContentType = "text/jsx";
|
|
||||||
else if (ext == ".json")
|
|
||||||
responseContentType = "application/json";
|
|
||||||
else if (ext == ".css")
|
|
||||||
responseContentType = "text/css";
|
|
||||||
else if (ext == ".png")
|
|
||||||
responseContentType = "image/png";
|
|
||||||
else if (ext == ".jpg")
|
|
||||||
responseContentType = "image/jpeg";
|
|
||||||
else if (ext == ".gif")
|
|
||||||
responseContentType = "image/gif";
|
|
||||||
else if (ext == ".txt")
|
|
||||||
responseContentType = "text/plain";
|
|
||||||
else if (ext == ".xml")
|
|
||||||
responseContentType = "text/xml";
|
|
||||||
else if (ext == ".svg")
|
|
||||||
responseContentType = "image/svg+xml";
|
|
||||||
else responseContentType = "application/octet-stream";
|
|
||||||
scode = OSUtils::readFile((_uiStaticPath + ZT_PATH_SEPARATOR_S + ps[0]).c_str(),responseBody) ? 200 : 404;
|
|
||||||
} else {
|
|
||||||
scode = 404;
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
} else if (isAuth) {
|
// parse out url args
|
||||||
/* Things that require authentication -- a.k.a. everything but static web app pages. */
|
int synotoken_pos = path.find("SynoToken");
|
||||||
|
int argpos = path.find("?");
|
||||||
if (ps[0] == "status") {
|
if(synotoken_pos != std::string::npos && argpos != std::string::npos) {
|
||||||
responseContentType = "application/json";
|
std::string cookie = path.substr(argpos+1, synotoken_pos-(argpos+1));
|
||||||
|
std::string synotoken = path.substr(synotoken_pos);
|
||||||
ZT_NodeStatus status;
|
std::string cookie_val = cookie.substr(cookie.find("=")+1);
|
||||||
_node->status(&status);
|
std::string synotoken_val = synotoken.substr(synotoken.find("=")+1);
|
||||||
|
// Set necessary env for auth script
|
||||||
std::string clusterJson;
|
std::map<std::string,std::string>::const_iterator ah2(headers.find("x-forwarded-for"));
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
setenv("HTTP_COOKIE", cookie_val.c_str(), true);
|
||||||
{
|
setenv("HTTP_X_SYNO_TOKEN", synotoken_val.c_str(), true);
|
||||||
ZT_ClusterStatus cs;
|
setenv("REMOTE_ADDR", ah2->second.c_str(),true);
|
||||||
_node->clusterStatus(&cs);
|
//fprintf(stderr, "HTTP_COOKIE: %s\n",std::getenv ("HTTP_COOKIE"));
|
||||||
|
//fprintf(stderr, "HTTP_X_SYNO_TOKEN: %s\n",std::getenv ("HTTP_X_SYNO_TOKEN"));
|
||||||
if (cs.clusterSize >= 1) {
|
//fprintf(stderr, "REMOTE_ADDR: %s\n",std::getenv ("REMOTE_ADDR"));
|
||||||
char t[1024];
|
// check synology web auth
|
||||||
Utils::snprintf(t,sizeof(t),"{\n\t\t\"myId\": %u,\n\t\t\"clusterSize\": %u,\n\t\t\"members\": [",cs.myId,cs.clusterSize);
|
char user[256], buf[1024];
|
||||||
clusterJson.append(t);
|
FILE *fp = NULL;
|
||||||
for(unsigned int i=0;i<cs.clusterSize;++i) {
|
bzero(user, 256);
|
||||||
Utils::snprintf(t,sizeof(t),"%s\t\t\t{\n\t\t\t\t\"id\": %u,\n\t\t\t\t\"msSinceLastHeartbeat\": %u,\n\t\t\t\t\"alive\": %s,\n\t\t\t\t\"x\": %d,\n\t\t\t\t\"y\": %d,\n\t\t\t\t\"z\": %d,\n\t\t\t\t\"load\": %llu,\n\t\t\t\t\"peers\": %llu\n\t\t\t}",
|
fp = popen("/usr/syno/synoman/webman/modules/authenticate.cgi", "r");
|
||||||
((i == 0) ? "\n" : ",\n"),
|
if(!fp)
|
||||||
cs.members[i].id,
|
isAuth = false;
|
||||||
cs.members[i].msSinceLastHeartbeat,
|
else {
|
||||||
(cs.members[i].alive != 0) ? "true" : "false",
|
bzero(buf, sizeof(buf));
|
||||||
cs.members[i].x,
|
fread(buf, 1024, 1, fp);
|
||||||
cs.members[i].y,
|
if(strlen(buf) > 0) {
|
||||||
cs.members[i].z,
|
snprintf(user, 256, "%s", buf);
|
||||||
cs.members[i].load,
|
isAuth = true;
|
||||||
cs.members[i].peers);
|
|
||||||
clusterJson.append(t);
|
|
||||||
}
|
}
|
||||||
clusterJson.append(" ]\n\t\t}");
|
}
|
||||||
|
pclose(fp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Utils::snprintf(json,sizeof(json),
|
if (httpMethod == HTTP_GET) {
|
||||||
"{\n"
|
if (isAuth) {
|
||||||
"\t\"address\": \"%.10llx\",\n"
|
if (ps[0] == "status") {
|
||||||
"\t\"publicIdentity\": \"%s\",\n"
|
ZT_NodeStatus status;
|
||||||
"\t\"worldId\": %llu,\n"
|
_node->status(&status);
|
||||||
"\t\"worldTimestamp\": %llu,\n"
|
|
||||||
"\t\"online\": %s,\n"
|
Utils::snprintf(tmp,sizeof(tmp),"%.10llx",status.address);
|
||||||
"\t\"tcpFallbackActive\": %s,\n"
|
res["address"] = tmp;
|
||||||
"\t\"versionMajor\": %d,\n"
|
res["publicIdentity"] = status.publicIdentity;
|
||||||
"\t\"versionMinor\": %d,\n"
|
res["online"] = (bool)(status.online != 0);
|
||||||
"\t\"versionRev\": %d,\n"
|
res["tcpFallbackActive"] = _svc->tcpFallbackActive();
|
||||||
"\t\"version\": \"%d.%d.%d\",\n"
|
res["versionMajor"] = ZEROTIER_ONE_VERSION_MAJOR;
|
||||||
"\t\"clock\": %llu,\n"
|
res["versionMinor"] = ZEROTIER_ONE_VERSION_MINOR;
|
||||||
"\t\"cluster\": %s\n"
|
res["versionRev"] = ZEROTIER_ONE_VERSION_REVISION;
|
||||||
"}\n",
|
res["versionBuild"] = ZEROTIER_ONE_VERSION_BUILD;
|
||||||
status.address,
|
Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d",ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
|
||||||
status.publicIdentity,
|
res["version"] = tmp;
|
||||||
status.worldId,
|
res["clock"] = OSUtils::now();
|
||||||
status.worldTimestamp,
|
|
||||||
(status.online) ? "true" : "false",
|
World planet(_node->planet());
|
||||||
(_svc->tcpFallbackActive()) ? "true" : "false",
|
res["planetWorldId"] = planet.id();
|
||||||
ZEROTIER_ONE_VERSION_MAJOR,
|
res["planetWorldTimestamp"] = planet.timestamp();
|
||||||
ZEROTIER_ONE_VERSION_MINOR,
|
|
||||||
ZEROTIER_ONE_VERSION_REVISION,
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION,
|
nlohmann::json cj;
|
||||||
(unsigned long long)OSUtils::now(),
|
ZT_ClusterStatus cs;
|
||||||
((clusterJson.length() > 0) ? clusterJson.c_str() : "null"));
|
_node->clusterStatus(&cs);
|
||||||
responseBody = json;
|
if (cs.clusterSize >= 1) {
|
||||||
|
nlohmann::json cja = nlohmann::json::array();
|
||||||
|
for(unsigned int i=0;i<cs.clusterSize;++i) {
|
||||||
|
nlohmann::json cjm;
|
||||||
|
cjm["id"] = (int)cs.members[i].id;
|
||||||
|
cjm["msSinceLastHeartbeat"] = cs.members[i].msSinceLastHeartbeat;
|
||||||
|
cjm["alive"] = (bool)(cs.members[i].alive != 0);
|
||||||
|
cjm["x"] = cs.members[i].x;
|
||||||
|
cjm["y"] = cs.members[i].y;
|
||||||
|
cjm["z"] = cs.members[i].z;
|
||||||
|
cjm["load"] = cs.members[i].load;
|
||||||
|
cjm["peers"] = cs.members[i].peers;
|
||||||
|
cja.push_back(cjm);
|
||||||
|
}
|
||||||
|
cj["members"] = cja;
|
||||||
|
cj["myId"] = (int)cs.myId;
|
||||||
|
cj["clusterSize"] = cs.clusterSize;
|
||||||
|
}
|
||||||
|
res["cluster"] = cj;
|
||||||
|
#else
|
||||||
|
res["cluster"] = nlohmann::json();
|
||||||
|
#endif
|
||||||
|
|
||||||
scode = 200;
|
scode = 200;
|
||||||
} else if (ps[0] == "config") {
|
} else if (ps[0] == "moon") {
|
||||||
responseContentType = "application/json";
|
std::vector<World> moons(_node->moons());
|
||||||
responseBody = "{}"; // TODO
|
if (ps.size() == 1) {
|
||||||
|
// Return [array] of all moons
|
||||||
|
|
||||||
|
res = nlohmann::json::array();
|
||||||
|
for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
|
||||||
|
nlohmann::json mj;
|
||||||
|
_moonToJson(mj,*m);
|
||||||
|
res.push_back(mj);
|
||||||
|
}
|
||||||
|
|
||||||
scode = 200;
|
scode = 200;
|
||||||
|
} else {
|
||||||
|
// Return a single moon by ID
|
||||||
|
|
||||||
|
const uint64_t id = Utils::hexStrToU64(ps[1].c_str());
|
||||||
|
for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
|
||||||
|
if (m->id() == id) {
|
||||||
|
_moonToJson(res,*m);
|
||||||
|
scode = 200;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
} else if (ps[0] == "network") {
|
} else if (ps[0] == "network") {
|
||||||
ZT_VirtualNetworkList *nws = _node->networks();
|
ZT_VirtualNetworkList *nws = _node->networks();
|
||||||
if (nws) {
|
if (nws) {
|
||||||
if (ps.size() == 1) {
|
if (ps.size() == 1) {
|
||||||
// Return [array] of all networks
|
// Return [array] of all networks
|
||||||
responseContentType = "application/json";
|
|
||||||
responseBody = "[\n";
|
res = nlohmann::json::array();
|
||||||
for(unsigned long i=0;i<nws->networkCount;++i) {
|
for(unsigned long i=0;i<nws->networkCount;++i) {
|
||||||
if (i > 0)
|
|
||||||
responseBody.append(",");
|
|
||||||
OneService::NetworkSettings localSettings;
|
OneService::NetworkSettings localSettings;
|
||||||
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
|
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
|
||||||
_jsonAppend(1,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
|
nlohmann::json nj;
|
||||||
|
_networkToJson(nj,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
|
||||||
|
res.push_back(nj);
|
||||||
}
|
}
|
||||||
responseBody.append("\n]\n");
|
|
||||||
scode = 200;
|
scode = 200;
|
||||||
} else if (ps.size() == 2) {
|
} else if (ps.size() == 2) {
|
||||||
// Return a single network by ID or 404 if not found
|
// Return a single network by ID or 404 if not found
|
||||||
uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
|
|
||||||
|
const uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
|
||||||
for(unsigned long i=0;i<nws->networkCount;++i) {
|
for(unsigned long i=0;i<nws->networkCount;++i) {
|
||||||
if (nws->networks[i].nwid == wantnw) {
|
if (nws->networks[i].nwid == wantnw) {
|
||||||
responseContentType = "application/json";
|
|
||||||
OneService::NetworkSettings localSettings;
|
OneService::NetworkSettings localSettings;
|
||||||
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
|
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
|
||||||
_jsonAppend(0,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
|
_networkToJson(res,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
|
||||||
responseBody.push_back('\n');
|
|
||||||
scode = 200;
|
scode = 200;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // else 404
|
|
||||||
|
} else scode = 404;
|
||||||
_node->freeQueryResult((void *)nws);
|
_node->freeQueryResult((void *)nws);
|
||||||
} else scode = 500;
|
} else scode = 500;
|
||||||
} else if (ps[0] == "peer") {
|
} else if (ps[0] == "peer") {
|
||||||
@@ -466,58 +380,79 @@ unsigned int ControlPlane::handleRequest(
|
|||||||
if (pl) {
|
if (pl) {
|
||||||
if (ps.size() == 1) {
|
if (ps.size() == 1) {
|
||||||
// Return [array] of all peers
|
// Return [array] of all peers
|
||||||
responseContentType = "application/json";
|
|
||||||
responseBody = "[\n";
|
res = nlohmann::json::array();
|
||||||
for(unsigned long i=0;i<pl->peerCount;++i) {
|
for(unsigned long i=0;i<pl->peerCount;++i) {
|
||||||
if (i > 0)
|
nlohmann::json pj;
|
||||||
responseBody.append(",\n");
|
_peerToJson(pj,&(pl->peers[i]));
|
||||||
_jsonAppend(1,responseBody,&(pl->peers[i]));
|
res.push_back(pj);
|
||||||
}
|
}
|
||||||
responseBody.append("\n]\n");
|
|
||||||
scode = 200;
|
scode = 200;
|
||||||
} else if (ps.size() == 2) {
|
} else if (ps.size() == 2) {
|
||||||
// Return a single peer by ID or 404 if not found
|
// Return a single peer by ID or 404 if not found
|
||||||
|
|
||||||
uint64_t wantp = Utils::hexStrToU64(ps[1].c_str());
|
uint64_t wantp = Utils::hexStrToU64(ps[1].c_str());
|
||||||
for(unsigned long i=0;i<pl->peerCount;++i) {
|
for(unsigned long i=0;i<pl->peerCount;++i) {
|
||||||
if (pl->peers[i].address == wantp) {
|
if (pl->peers[i].address == wantp) {
|
||||||
responseContentType = "application/json";
|
_peerToJson(res,&(pl->peers[i]));
|
||||||
_jsonAppend(0,responseBody,&(pl->peers[i]));
|
|
||||||
responseBody.push_back('\n');
|
|
||||||
scode = 200;
|
scode = 200;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // else 404
|
|
||||||
|
} else scode = 404;
|
||||||
_node->freeQueryResult((void *)pl);
|
_node->freeQueryResult((void *)pl);
|
||||||
} else scode = 500;
|
} else scode = 500;
|
||||||
} else if (ps[0] == "newIdentity") {
|
|
||||||
// Return a newly generated ZeroTier identity -- this is primarily for debugging
|
|
||||||
// and testing to make it easy for automated test scripts to generate test IDs.
|
|
||||||
Identity newid;
|
|
||||||
newid.generate();
|
|
||||||
responseBody = newid.toString(true);
|
|
||||||
responseContentType = "text/plain";
|
|
||||||
scode = 200;
|
|
||||||
} else {
|
} else {
|
||||||
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
|
if (_controller) {
|
||||||
if (_controller)
|
|
||||||
scode = _controller->handleControlPlaneHttpGET(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
|
scode = _controller->handleControlPlaneHttpGET(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
|
||||||
else scode = 404;
|
} else scode = 404;
|
||||||
#else
|
|
||||||
scode = 404;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else scode = 401; // isAuth == false
|
} else scode = 401; // isAuth == false
|
||||||
|
|
||||||
} else if ((httpMethod == HTTP_POST)||(httpMethod == HTTP_PUT)) {
|
} else if ((httpMethod == HTTP_POST)||(httpMethod == HTTP_PUT)) {
|
||||||
|
|
||||||
if (isAuth) {
|
if (isAuth) {
|
||||||
|
|
||||||
if (ps[0] == "config") {
|
if (ps[0] == "moon") {
|
||||||
// TODO
|
if (ps.size() == 2) {
|
||||||
|
|
||||||
|
uint64_t seed = 0;
|
||||||
|
try {
|
||||||
|
nlohmann::json j(OSUtils::jsonParse(body));
|
||||||
|
if (j.is_object()) {
|
||||||
|
seed = Utils::hexStrToU64(OSUtils::jsonString(j["seed"],"0").c_str());
|
||||||
|
}
|
||||||
|
} catch ( ... ) {
|
||||||
|
// discard invalid JSON
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<World> moons(_node->moons());
|
||||||
|
const uint64_t id = Utils::hexStrToU64(ps[1].c_str());
|
||||||
|
for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
|
||||||
|
if (m->id() == id) {
|
||||||
|
_moonToJson(res,*m);
|
||||||
|
scode = 200;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((scode != 200)&&(seed != 0)) {
|
||||||
|
char tmp[64];
|
||||||
|
Utils::snprintf(tmp,sizeof(tmp),"%.16llx",id);
|
||||||
|
res["id"] = tmp;
|
||||||
|
res["roots"] = nlohmann::json::array();
|
||||||
|
res["timestamp"] = 0;
|
||||||
|
res["signature"] = nlohmann::json();
|
||||||
|
res["updatesMustBeSignedBy"] = nlohmann::json();
|
||||||
|
res["waiting"] = true;
|
||||||
|
_node->orbit(id,seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else scode = 404;
|
||||||
} else if (ps[0] == "network") {
|
} else if (ps[0] == "network") {
|
||||||
if (ps.size() == 2) {
|
if (ps.size() == 2) {
|
||||||
|
|
||||||
uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
|
uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
|
||||||
_node->join(wantnw,(void *)0); // does nothing if we are a member
|
_node->join(wantnw,(void *)0); // does nothing if we are a member
|
||||||
ZT_VirtualNetworkList *nws = _node->networks();
|
ZT_VirtualNetworkList *nws = _node->networks();
|
||||||
@@ -527,55 +462,47 @@ unsigned int ControlPlane::handleRequest(
|
|||||||
OneService::NetworkSettings localSettings;
|
OneService::NetworkSettings localSettings;
|
||||||
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
|
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
|
||||||
|
|
||||||
json_value *j = json_parse(body.c_str(),body.length());
|
try {
|
||||||
if (j) {
|
nlohmann::json j(OSUtils::jsonParse(body));
|
||||||
if (j->type == json_object) {
|
if (j.is_object()) {
|
||||||
for(unsigned int k=0;k<j->u.object.length;++k) {
|
nlohmann::json &allowManaged = j["allowManaged"];
|
||||||
if (!strcmp(j->u.object.values[k].name,"allowManaged")) {
|
if (allowManaged.is_boolean()) localSettings.allowManaged = (bool)allowManaged;
|
||||||
if (j->u.object.values[k].value->type == json_boolean)
|
nlohmann::json &allowGlobal = j["allowGlobal"];
|
||||||
localSettings.allowManaged = (j->u.object.values[k].value->u.boolean != 0);
|
if (allowGlobal.is_boolean()) localSettings.allowGlobal = (bool)allowGlobal;
|
||||||
} else if (!strcmp(j->u.object.values[k].name,"allowGlobal")) {
|
nlohmann::json &allowDefault = j["allowDefault"];
|
||||||
if (j->u.object.values[k].value->type == json_boolean)
|
if (allowDefault.is_boolean()) localSettings.allowDefault = (bool)allowDefault;
|
||||||
localSettings.allowGlobal = (j->u.object.values[k].value->u.boolean != 0);
|
|
||||||
} else if (!strcmp(j->u.object.values[k].name,"allowDefault")) {
|
|
||||||
if (j->u.object.values[k].value->type == json_boolean)
|
|
||||||
localSettings.allowDefault = (j->u.object.values[k].value->u.boolean != 0);
|
|
||||||
}
|
}
|
||||||
}
|
} catch ( ... ) {
|
||||||
}
|
// discard invalid JSON
|
||||||
json_value_free(j);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_svc->setNetworkSettings(nws->networks[i].nwid,localSettings);
|
_svc->setNetworkSettings(nws->networks[i].nwid,localSettings);
|
||||||
|
_networkToJson(res,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
|
||||||
|
|
||||||
responseContentType = "application/json";
|
|
||||||
_jsonAppend(0,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
|
|
||||||
responseBody.push_back('\n');
|
|
||||||
scode = 200;
|
scode = 200;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_node->freeQueryResult((void *)nws);
|
_node->freeQueryResult((void *)nws);
|
||||||
} else scode = 500;
|
} else scode = 500;
|
||||||
}
|
|
||||||
|
} else scode = 404;
|
||||||
} else {
|
} else {
|
||||||
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
|
|
||||||
if (_controller)
|
if (_controller)
|
||||||
scode = _controller->handleControlPlaneHttpPOST(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
|
scode = _controller->handleControlPlaneHttpPOST(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
|
||||||
else scode = 404;
|
else scode = 404;
|
||||||
#else
|
|
||||||
scode = 404;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else scode = 401; // isAuth == false
|
} else scode = 401; // isAuth == false
|
||||||
|
|
||||||
} else if (httpMethod == HTTP_DELETE) {
|
} else if (httpMethod == HTTP_DELETE) {
|
||||||
|
|
||||||
if (isAuth) {
|
if (isAuth) {
|
||||||
|
|
||||||
if (ps[0] == "config") {
|
if (ps[0] == "moon") {
|
||||||
// TODO
|
if (ps.size() == 2) {
|
||||||
|
_node->deorbit(Utils::hexStrToU64(ps[1].c_str()));
|
||||||
|
res["result"] = true;
|
||||||
|
scode = 200;
|
||||||
|
} // else 404
|
||||||
} else if (ps[0] == "network") {
|
} else if (ps[0] == "network") {
|
||||||
ZT_VirtualNetworkList *nws = _node->networks();
|
ZT_VirtualNetworkList *nws = _node->networks();
|
||||||
if (nws) {
|
if (nws) {
|
||||||
@@ -584,8 +511,7 @@ unsigned int ControlPlane::handleRequest(
|
|||||||
for(unsigned long i=0;i<nws->networkCount;++i) {
|
for(unsigned long i=0;i<nws->networkCount;++i) {
|
||||||
if (nws->networks[i].nwid == wantnw) {
|
if (nws->networks[i].nwid == wantnw) {
|
||||||
_node->leave(wantnw,(void **)0);
|
_node->leave(wantnw,(void **)0);
|
||||||
responseBody = "true";
|
res["result"] = true;
|
||||||
responseContentType = "application/json";
|
|
||||||
scode = 200;
|
scode = 200;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -594,22 +520,21 @@ unsigned int ControlPlane::handleRequest(
|
|||||||
_node->freeQueryResult((void *)nws);
|
_node->freeQueryResult((void *)nws);
|
||||||
} else scode = 500;
|
} else scode = 500;
|
||||||
} else {
|
} else {
|
||||||
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
|
|
||||||
if (_controller)
|
if (_controller)
|
||||||
scode = _controller->handleControlPlaneHttpDELETE(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
|
scode = _controller->handleControlPlaneHttpDELETE(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
|
||||||
else scode = 404;
|
else scode = 404;
|
||||||
#else
|
|
||||||
scode = 404;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
scode = 401; // isAuth = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else scode = 401; // isAuth = false
|
||||||
} else {
|
} else {
|
||||||
scode = 400;
|
scode = 400;
|
||||||
responseBody = "Method not supported.";
|
}
|
||||||
|
|
||||||
|
if (responseBody.length() == 0) {
|
||||||
|
if ((res.is_object())||(res.is_array()))
|
||||||
|
responseBody = OSUtils::jsonDump(res);
|
||||||
|
else responseBody = "{}";
|
||||||
|
responseContentType = "application/json";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap result in jsonp function call if the user included a jsonp= url argument.
|
// Wrap result in jsonp function call if the user included a jsonp= url argument.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user