updated included zerotierone src

This commit is contained in:
Joseph Henry
2016-10-21 15:44:36 -07:00
parent 3dfea66bd4
commit 12bd9439db
105 changed files with 5083 additions and 6969 deletions

View File

@@ -1,12 +1,12 @@
## Primary Authors ## Primary Authors
* ZeroTier Core and ZeroTier One virtual networking service<br> * ZeroTier protocol design and core network virtualization engine, ZeroTier One service, React web UI, packaging for most platforms, kitchen sink...<br>
Adam Ierymenko / adam.ierymenko@zerotier.com Adam Ierymenko / adam.ierymenko@zerotier.com
* Java JNI Interface to enable Android application development, and Android app itself (code for that is elsewhere)<br> * Java JNI Interface to enable Android application development, and Android app itself (code for that is elsewhere)<br>
Grant Limberg / glimberg@gmail.com Grant Limberg / glimberg@gmail.com
* ZeroTier SDK (formerly known as Network Containers)<br> * Network Containers for Linux, iOS, Android<br>
Joseph Henry / joseph.henry@zerotier.com Joseph Henry / joseph.henry@zerotier.com
## Third Party Contributors ## Third Party Contributors
@@ -25,8 +25,6 @@
## 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.
* LZ4 compression algorithm by Yann Collet * LZ4 compression algorithm by Yann Collet
* Files: ext/lz4/* * Files: ext/lz4/*
@@ -39,26 +37,25 @@ 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
* C++11 json (nlohmann/json) by Niels Lohmann * json-parser by James McLaughlin
* Files: ext/json/* * Files: ext/json-parser/*
* Home page: https://github.com/nlohmann/json * Home page: https://github.com/udp/json-parser/
* License grant: MIT * License grant: BSD attribution
* TunTapOSX by Mattias Nissler * TunTapOSX by Mattias Nissler
* Files: ext/tap-mac/tuntap/* * Files: ext/tap-mac/tuntap/*
* Home page: http://tuntaposx.sourceforge.net/ * Home page: http://tuntaposx.sourceforge.net/
* License grant: BSD attribution no-endorsement * License grant: BSD attribution no-endorsement
* ZeroTier Modifications: change interface name to zt#, increase max MTU, increase max devices
* tap-windows6 by the OpenVPN project * tap-windows and tap-windows6 by the OpenVPN project
* Files: windows/TapDriver6/* * Files: windows/TapDriver6/*
* Home page: * Home page:
https://github.com/OpenVPN/tap-windows/
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)
* 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 digital signature algorithm, and Poly1305 MAC algorithm, all by
@@ -71,7 +68,7 @@ These are included in ext/ for platforms that do not have them available in comm
* Home page: http://cr.yp.to/ * Home page: http://cr.yp.to/
* License grant: public domain * License grant: public domain
* MiniUPNPC and libnatpmp by Thomas Bernard * MiniUPNPC by Thomas Bernard
* Files: * Files:
ext/libnatpmp/* ext/libnatpmp/*

View File

@@ -1,54 +1,35 @@
ZeroTier - A Planetary Ethernet Switch ZeroTier One
====== ======
ZeroTier is a software-based managed Ethernet switch for planet Earth. ZeroTier is a software defined networking layer for 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 can be used for on-premise network virtualization, as a peer to peer VPN for mobile teams, for hybrid or multi-data-center cloud deployments, or just about anywhere else secure software defined virtual networking is useful.
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/). ZeroTier One is our OS-level client service. It allows Mac, Linux, Windows, FreeBSD, and soon other types of clients to join ZeroTier virtual networks like conventional VPNs or VLANs. It can run on native systems, VMs, or containers (Docker, OpenVZ, etc.).
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. You can also download professionally packaged binary installers/packages for a variety of supported OSes there if you don't want to build ZeroTier One from source.
### Getting Started
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.
For testing we provide a public virtual network called *Earth* with network ID `8056c2e21c000001`. On Linux and Mac you can do this with:
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.
*(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
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.
### Building from Source ### Building from Source
For Mac, Linux, and BSD, just type `make` (or `gmake` on BSD). Platform requirements are: 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:
- **Mac**: Xcode command line tools for OSX 10.7 or newer. * Mac: Xcode command line tools, and [Packages](http://s.sudre.free.fr/Software/Packages/about.html) if you want to build an OSX .pkg installer ("make mac-dist-pkg"). It should build on OSX 10.7 or newer.
- **Linux**: GCC/G++ 4.9 or newer or CLANG 3.4 or newer, and GNU make and standard shell tools of course. * Linux: gcc/g++ or clang/clang++ (Makefile will use clang by default if available.)
- The Linux make file will auto-detect and prefer CLANG if present since in our experience it does a better job optimizing C++ code. You can specify CC= and CXX= on the make command line to override this behavior. * FreeBSD (and other BSD): C++ compiler (G++ usually) and GNU make (gmake).
- Several distributions including CentOS 7 ship with G++ 4.8 which has broken C++11 support. If you are running CentOS 7 type `sudo yum install clang` to install CLANG 3.4, which will work fine. There is no C++11 in the ZeroTier core but we have started using it in the service and network controller code.
- If any of the following have development headers on the system they are auto-detected and the build will link against system versions: `http-parser`, `lz4`, `libnatpmp`, and `miniupnpc`. If these are missing (or too old) the versions in `ext/` are used and are statically linked into the binary. Please be aware of this since if you build with these present the resulting binary will require them on other systems too.
- **FreeBSD**: GCC/G++ 4.9 or newer and `gmake` since our make files use GNU extensions.
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. 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.
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. Build steps for Windows are a bit more complicated. For the moment you are on your own there.
Windows, of course, is special. A Visual Studio solution file is located in the *windows/* subfolder. We have not tried MinGW or CLANG for Windows but these may work, though you will have issues building the Windows GUI with them. Mobile versions are in progress. They don't work yet, and in any case only the glue code will be included in this repository. The full mobile apps are in private repositories on our own git server.
Mac and Windows require tun/tap virtual network port drivers. We include pre-built signed binaries for these in `ext/bin`, so you should not need to build these yourself. Linux, FreeBSD, and others can use built-in kernel support for tun/tap network devices. ### Supported Platforms
CPU architecture shouldn't matter unless it's smaller than 32-bit or something really bizarre like a "middle-endian" processor. We have reports of ZeroTier One running on arm32, arm64, and MIPS. It builds and runs out of the box on Raspberry Pi, BeagleBone, BananaPi, and other ARM-based developer/hobbyist boards.
ZeroTier is written in C and C++ (C++03 / ISO/IEC 14882:2003) and uses data structures and algorithms from the C++03 STL. We do not use any C++11 features (yet), since we want to support a few old and embedded platforms that don't have C++11 compilers. You *will* require a compiler and headers new enough to support 64-bit integers (long long) and the *stdint.h* header. The latter could also be faked by adding defines for things like *uint32\_t*, *int64\_t*, etc.
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.
### Running ### Running
@@ -64,10 +45,10 @@ 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` * BSD: /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.)
Running ZeroTier One on a Mac is the same, but OSX requires a kernel extension. We ship a signed binary build of the ZeroTier tap device driver, which can be installed on Mac with: Running ZeroTier One on a Mac is the same, but OSX requires a kernel extension. We ship a signed binary build of the ZeroTier tap device driver, which can be installed on Mac with:
@@ -75,6 +56,96 @@ Running ZeroTier One on a Mac is the same, but OSX requires a kernel extension.
This will create the home folder for Mac, place *tap.kext* there, and set its modes correctly to enable ZeroTier One to manage it with *kextload* and *kextunload*. This will create the home folder for Mac, place *tap.kext* there, and set its modes correctly to enable ZeroTier One to manage it with *kextload* and *kextunload*.
We recommend using our binary packages on Windows, since there are several prerequisites such as a tap driver that must be installed on the system *and* in the home folder.
### Joining A Network
ZeroTier virtual networks are identified by 16-digit hexadecimal network IDs, while devices are identified by 10-digit addresses. To get your address run:
sudo zerotier-cli status
(Use ./zerotier-cli if you're running it right from your build folder.)
You should see something like:
200 info ########## ONLINE #.#.#
That 10-digit hex code is you. It's derived via a one-way proof of work function from your cryptographic public key. Your public key can be found in *identity.public* in ZeroTier's home folder, while *identity.secret* contains your full identity including the secret portion of the key pair.
(The identity files define your device's *identity*. Moving them to another system will move that identity. Be careful when cloning virtual machines that have identities stored on them. If two devices have the same identity, they'll "fight" over it and you won't know which device will receive network packets.)
If you want to do a quick test, you can join [Earth](https://www.zerotier.com/earth.html). It's a global public network that anyone can join. Type:
sudo zerotier-cli join 8056c2e21c000001
Then:
sudo zerotier-cli listnetworks
At first it'll be in *REQUESTING\_CONFIGURATION* state. In a few seconds to a minute you should see something like:
200 listnetworks 8056c2e21c000001 earth.zerotier.net ##:##:##:##:##:## OK PUBLIC zt0 ##.##.##.##/##
Earth will assign you an IP address in the "unofficially available" globally unrouted 28.0.0.0/7 IP block so as to avoid conflicts with local networks. (Your networks can use any IP scheme, or can even leave IP addresses unmanaged.) Once you get an IP, you should be able to ping something:
ping earth.zerotier.net
Go to [http://earth.zerotier.net/](http://earth.zerotier.net/) to see a short little welcome page that will tell you your IP and Ethernet MAC address.
Earth is a public place. If you don't want to stick around run:
sudo zerotier-cli leave 8056c2e21c000001
The network (and associated interface) should be gone.
Networks are created and administrated by network controllers. Most users will want to use our hosted controllers. Visit [our web site](https://www.zerotier.com/) for more information. Later in this README there are brief instructions about building ZeroTier One with network controller support for those who want to try running their own.
Macintosh and Windows installers also install a GUI application.
### Installing
We don't have a "make install" rule quite yet. On Linux you can type:
make installer
This will build a binary that, when run, will install ZeroTier One on most current Linux distributions. We also have RPM and DEB build files in *ext/installfiles/linux* that wrap this installer in packages for RedHat/CentOS and Debian/Ubuntu derived distributions. If *rpmbuild* is present on the system, the RPM will be built. If *dpkg-deb* is present, the DEB package will be built.
On Mac the best way is to install [Packages](http://s.sudre.free.fr/Software/Packages/about.html) and use:
make mac-dist-pkg
This builds a .pkg file that can be installed.
In FreeBSD there is now an official .pkg in the FreeBSD repository. Type "pkg install zerotier". It can also be built and installed from source.
Linux/BSD and Mac installations have an *uninstall.sh* file in their ZeroTier home folder that cleanly removes ZeroTier One from the system. Run this with:
sudo /path/to/ZeroTier/home/folder/uninstall.sh
Windows installers are insane. We build our .MSI installers with [Advanced Installer Enterprise](http://www.advancedinstaller.com). The Advanced Installer project file is in *ext/installfiles/windows*. To avoid lasting psychological trauma we recommend leaving Windows installers alone and using the pre-built Windows binaries on our web site.
### Using ZeroTier One in Docker Containers
To run the ZeroTier One service in a Docker container, run it with "--device=/dev/net/tun --cap-add=NET_ADMIN". This will allow ZeroTier One to open a "tap" virtual network port inside the container.
Alternately, you can use Ethernet bridging to bridge the *docker0* device on your system to a ZeroTier virtual network. This allows you to run ZeroTier One on the host and bridge the entire Docker network backplane to a virtual network or other hosts.
We're working on better "official" Docker support. In the meantime there is a [user-contributed project here](https://github.com/davide/docker-zerotier).
### Building with Network Controller Support
**Warning: as of beta version 1.0.3 the new network controller is not heavily tested. We recommend waiting for 1.0.4 to deploy this in production.**
Network controllers are nodes responsible for issuing configurations and certificates to members of ZeroTier virtual networks. Most users won't need to run their own, so this code is by default not included in the ZeroTier One binary.
You can build a network controller on Linux or Mac with:
make ZT_ENABLE_NETWORK_CONTROLLER=1
This will build a version that contains the Sqlite-backed network controller and associated extensions to the JSON local service control API. You will need the development headers for sqlite3 installed. On Mac these ship as part of Xcode, while on Linux they'll be found in packages for the various distributions.
See the JSON API documentation in [service/](service/) for more information about how to control controllers.
### Troubleshooting ### Troubleshooting
For most users, it just works. For most users, it just works.
@@ -85,7 +156,7 @@ The Mac firewall can be found under "Security" in System Preferences. Linux has
sudo ufw allow 9993/udp sudo ufw allow 9993/udp
On CentOS check `/etc/sysconfig/iptables` for IPTables rules. For other distributions consult your distribution's documentation. You'll also have to check the UIs or documentation for commercial third party firewall applications like Little Snitch (Mac), McAfee Firewall Enterprise (Windows), etc. if you are running any of those. Some corporate environments might have centrally managed firewall software, so you might also have to contact IT. On CentOS check */etc/sysconfig/iptables* for IPTables rules. For other distributions consult your distribution's documentation. You'll also have to check the UIs or documentation for commercial third party firewall applications like Little Snitch (Mac), McAfee Firewall Enterprise (Windows), etc. if you are running any of those. Some corporate environments might have centrally managed firewall software, so you might also have to contact IT.
ZeroTier One peers will automatically locate each other and communicate directly over a local wired LAN *if UDP port 9993 inbound is open*. If that port is filtered, they won't be able to see each others' LAN announcement packets. If you're experiencing poor performance between devices on the same physical network, check their firewall settings. Without LAN auto-location peers must attempt "loopback" NAT traversal, which sometimes fails and in any case requires that every packet traverse your external router twice. ZeroTier One peers will automatically locate each other and communicate directly over a local wired LAN *if UDP port 9993 inbound is open*. If that port is filtered, they won't be able to see each others' LAN announcement packets. If you're experiencing poor performance between devices on the same physical network, check their firewall settings. Without LAN auto-location peers must attempt "loopback" NAT traversal, which sometimes fails and in any case requires that every packet traverse your external router twice.
@@ -97,7 +168,7 @@ If a firewall between you and the Internet blocks ZeroTier's UDP traffic, you wi
### Contributing ### Contributing
Please make pull requests against the `dev` branch. The `master` branch is release, and `edge` is for unstable and work in progress changes and is not likely to work. There are three main branches: **edge**, **test**, and **master**. Other branches may be for specific features, tests, or use cases. In general **edge** is "bleeding" and may or may not work, while **test** should be relatively stable and **master** is the latest tagged release. Pull requests should generally be done against **test** or **edge**, since pull requests against **master** may be working against a branch that is somewhat out of date.
### License ### License

View File

@@ -43,6 +43,7 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>

View File

@@ -43,6 +43,7 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/sys_domain.h> #include <sys/sys_domain.h>

View File

@@ -1,253 +1,32 @@
Network Controller Microservice Network Controller Implementation
====== ======
Every ZeroTier virtual network has a *network controller*. This is our reference implementation and is the same one we use to power our own hosted services at [my.zerotier.com](https://my.zerotier.com/). Network controllers act as configuration servers and certificate authorities for the members of networks. Controllers are located on the network by simply parsing out the first 10 digits of a network's 16-digit network ID: these are the address of the controller. This folder contains code implementing the node/NetworkController.hpp interface to allow ZeroTier nodes to create and manage virtual networks.
As of ZeroTier One version 1.2.0 this code is included in normal builds for desktop, laptop, and server (Linux, etc.) targets, allowing any device to create virtual networks without having to be rebuilt from source with special flags to enable this feature. While this does offer a convenient way to create ad-hoc networks or experiment, we recommend running a dedicated controller somewhere secure and stable for any "serious" use case. ### Building
Controller data is stored in JSON format under `controller.d` in the ZeroTier working directory. It can be copied, rsync'd, placed in `git`, etc. The files under `controller.d` should not be modified in place while the controller is running or data loss may result, and if they are edited directly take care not to save corrupt JSON since that can also lead to data loss when the controller is restarted. Going through the API is strongly preferred to directly modifying these files. By default this code is not built or included in the client. To build on Linux, BSD, or Mac add `ZT_ENABLE_NETWORK_CONTROLLER=1` to the make command line. You'll need the development headers for Sqlite3 installed. They ship as part of OSX and Xcode. On Linux or BSD you'll probably need to install a package.
### Upgrading from Older Versions ### Running
Older versions of this code used a SQLite database instead of in-filesystem JSON. A migration utility called `migrate-sqlite` is included here and *must* be used to migrate this data to the new format. If the controller is started with an old `controller.db` in its working directory it will terminate after printing an error to *stderr*. This is done to prevent "surprises" for those running DIY controllers using the old code. When started, a controller-enabled build of ZeroTier One will automatically create and initialize a *controller.db* in its home folder. This is where all the controller's data and persistent state lives.
The migration tool is written in nodeJS and can be used like this: Since Sqlite3 supports multiple processes attached to the same database, it is safe to back up a running database with the command line *sqlite3* utility:
cd migrate-sqlite sqlite3 /path/to/controller.db .dump
npm install
node migrate-sqlite.js <path to ZeroTier working directory>
You may need to `sudo node ...` if the ZeroTier working directory is owned by root. In production ZeroTier runs this frequently and keeps many timestamped copies going back about a week. These are also backed up (encrypted) to Amazon S3 along with the rest of our data.
This code will dump the contents of any `controller.db` in the ZeroTier working directory and recreate its data in the form of JSON objects under `controller.d`. The old `controller.db` will then be renamed to `controller.db.migrated` and the controller will start normally. ### Administrating
After migrating make sure that the contents of `controller.d` are owned and writable by the user that will be running the ZeroTier controller process! (Usually this is root but controllers that don't also join networks are sometimes run as unprivileged users.) See service/README.md for documentation on the JSON API presented by this network controller implementation. Also see *nodejs-zt1-client* for a NodeJS JavaScript interface.
If you don't have nodeJS on the machine running ZeroTier it is perfectly fine to just copy `controller.db` to a directory on another machine and run the migration tool there. After running your migration the contents of `controller.d` can be tar'd up and copied back over to the controller. Just remember to rename or remove `controller.db` on the controller machine afterwords so the controller will start. ### Reliability
### Scalability and Reliability Network controllers can go offline without affecting already-configured members of running networks. You just won't be able to change anything and new members will not be able to join.
Controllers can in theory host up to 2^24 networks and serve many millions of devices (or more), but we recommend spreading large numbers of networks across many controllers for load balancing and fault tolerance reasons. Since the controller uses the filesystem as its data store we recommend fast filesystems and fast SSD drives for heavily loaded controllers. High-availability can be implemented through fail-over. A simple method involves making a frequent backup of the SQLite database (use the SQLite command line client to do this safely) and the network configuration master's working directory. Then, if the master goes down, another instance of it can rapidly be provisioned elsewhere. Since ZeroTier addresses are mobile, the new instance will quickly (usually no more than 30s) take over for the old one and service requests.
Since ZeroTier nodes are mobile and do not need static IPs, implementing high availability fail-over for controllers is easy. Just replicate their working directories from master to backup and have something automatically fire up the backup if the master goes down. Many modern orchestration tools have built-in support for this. It would also be possible in theory to run controllers on a replicated or distributed filesystem, but we haven't tested this yet. ### Limits
### Dockerizing Controllers A single network configuration master can administrate up to 2^24 (~16m) networks as per the ZeroTier protocol limit. There is no hard limit on the number of clients, though millions or more would impose significant CPU demands on a server. Optimizations could be implemented such as memoization/caching to reduce this.
ZeroTier network controllers can easily be run in Docker or other container systems. Since containers do not need to actually join networks, extra privilege options like "--device=/dev/net/tun --privileged" are not needed. You'll just need to map the local JSON API port of the running controller and allow it to access the Internet (over UDP/9993 at a minimum) so things can reach and query it.
### Network Controller API
The controller API is hosted via the same JSON API endpoint that ZeroTier One uses for local control (usually at 127.0.0.1 port 9993). All controller options are routed under the `/controller` base path.
The controller microservice does not implement any fine-grained access control (authentication is via authtoken.secret just like the regular JSON API) or other complex mangement features. It just takes network and network member configurations and reponds to controller queries. We have an enterprise product called [ZeroTier Central](https://my.zerotier.com/) that we host as a service (and that companies can license to self-host) that does this.
All working network IDs on a controller must begin with the controller's ZeroTier address. The API will *allow* "foreign" networks to be added but the controller will have no way of doing anything with them since nobody will know to query it. (In the future we might support secondaries, which would make this relevant.)
The JSON API is *very* sensitive about types. Integers must be integers and strings strings, etc. Incorrectly typed and unrecognized fields may result in ignored fields or a 400 (bad request) error.
#### `/controller`
* Purpose: Check for controller function and return controller status
* Methods: GET
* Returns: { object }
| Field | Type | Description | Writable |
| ------------------ | ----------- | ------------------------------------------------- | -------- |
| controller | boolean | Always 'true' | no |
| apiVersion | integer | Controller API version, currently 3 | no |
| clock | integer | Current clock on controller, ms since epoch | no |
#### `/controller/network`
* Purpose: List all networks hosted by this controller
* Methods: GET
* Returns: [ string, ... ]
This returns an array of 16-digit hexadecimal network IDs.
#### `/controller/network/<network ID>`
* Purpose: Create, configure, and delete hosted networks
* Methods: GET, POST, DELETE
* Returns: { object }
By making queries to this path you can create, configure, and delete networks. DELETE is final, so don't do it unless you really mean it.
When POSTing new networks take care that their IDs are not in use, otherwise you may overwrite an existing one. To create a new network with a random unused ID, POST to `/controller/network/##########______`. The #'s are the controller's 10-digit ZeroTier address and they're followed by six underscores. Check the `nwid` field of the returned JSON object for your network's newly allocated ID. Subsequent POSTs to this network must refer to its actual path.
| Field | Type | Description | Writable |
| --------------------- | ------------- | ------------------------------------------------- | -------- |
| id | string | 16-digit network ID | no |
| nwid | string | 16-digit network ID (old, but still around) | no |
| clock | integer | Current clock, ms since epoch | no |
| name | string | A short name for this network | YES |
| private | boolean | Is access control enabled? | YES |
| enableBroadcast | boolean | Ethernet ff:ff:ff:ff:ff:ff allowed? | YES |
| allowPassiveBridging | boolean | Allow any member to bridge (very experimental) | YES |
| v4AssignMode | object | IPv4 management and assign options (see below) | YES |
| v6AssignMode | object | IPv6 management and assign options (see below) | YES |
| multicastLimit | integer | Maximum recipients for a multicast packet | YES |
| creationTime | integer | Time network was first created | no |
| revision | integer | Network config revision counter | no |
| authorizedMemberCount | integer | Number of authorized members (for private nets) | no |
| activeMemberCount | integer | Number of members that appear to be online | no |
| totalMemberCount | integer | Total known members of this network | no |
| routes | array[object] | Managed IPv4 and IPv6 routes; see below | YES |
| ipAssignmentPools | array[object] | IP auto-assign ranges; see below | YES |
| rules | array[object] | Traffic rules; see below | YES |
Recent changes:
* The `ipLocalRoutes` field appeared in older versions but is no longer present. Routes will now show up in `routes`.
* The `relays` field is gone since network preferred relays are gone. This capability is replaced by VL1 level federation ("federated roots").
Other important points:
* Networks without rules won't carry any traffic. If you don't specify any on network creation an "accept anything" rule set will automatically be added.
* Managed IP address assignments and IP assignment pools that do not fall within a route configured in `routes` are ignored and won't be used or sent to members.
* The default for `private` is `true` and this is probably what you want. Turning `private` off means *anyone* can join your network with only its 16-digit network ID. It's also impossible to de-authorize a member as these networks don't issue or enforce certificates. Such "party line" networks are used for decentralized app backplanes, gaming, and testing but are otherwise not common.
**Auto-Assign Modes:**
Auto assign modes (`v4AssignMode` and `v6AssignMode`) contain objects that map assignment modes to booleans.
For IPv4 the only valid setting is `zt` which, if true, causes IPv4 addresses to be auto-assigned from `ipAssignmentPools` to members that do not have an IPv4 assignment. Note that active bridges are exempt and will not get auto-assigned IPs since this can interfere with bridging. (You can still manually assign one if you want.)
IPv6 includes this option and two others: `6plane` and `rfc4193`. These assign private IPv6 addresses to each member based on a deterministic assignment scheme that allows members to emulate IPv6 NDP to skip multicast for better performance and scalability. The `rfc4193` mode gives every member a /128 on a /88 network, while `6plane` gives every member a /80 within a /40 network but uses NDP emulation to route *all* IPs under that /80 to its owner. The `6plane` mode is great for use cases like Docker since it allows every member to assign IPv6 addresses within its /80 that just work instantly and globally across the network.
**IP assignment pool object format:**
| Field | Type | Description |
| --------------------- | ------------- | ------------------------------------------------- |
| ipRangeStart | string | Starting IP address in range |
| ipRangeEnd | string | Ending IP address in range (inclusive) |
Pools are only used if auto-assignment is on for the given address type (IPv4 or IPv6) and if the entire range falls within a managed route.
IPv6 ranges work just like IPv4 ranges and look like this:
{
"ipRangeStart": "fd00:feed:feed:beef:0000:0000:0000:0000",
"ipRangeEnd": "fd00:feed:feed:beef:ffff:ffff:ffff:ffff"
}
(You can POST a shortened-form IPv6 address but the API will always report back un-shortened canonical form addresses.)
That defines a range within network `fd00:feed:feed:beef::/64` that contains up to 2^64 addresses. If an IPv6 range is large enough, the controller will assign addresses by placing each member's device ID into the address in a manner similar to the RFC4193 and 6PLANE modes. Otherwise it will assign addresses at random.
**Rule object format:**
Each rule is actually a sequence of zero or more `MATCH_` entries in the rule array followed by an `ACTION_` entry that describes what to do if all the preceding entries match. An `ACTION_` without any preceding `MATCH_` entries is always taken, so setting a single `ACTION_ACCEPT` rule yields a network that allows all traffic. If no rules are present the default action is `ACTION_DROP`.
Rules are evaluated in the order in which they appear in the array. There is currently a limit of 256 entries per network. Capabilities should be used if a larger and more complex rule set is needed since they allow rules to be grouped by purpose and only shipped to members that need them.
Each rule table entry has two common fields.
| Field | Type | Description |
| --------------------- | ------------- | ------------------------------------------------- |
| type | string | Entry type (all caps, case sensitive) |
| not | boolean | If true, MATCHes match if they don't match |
The following fields may or may not be present depending on rule type:
| Field | Type | Description |
| --------------------- | ------------- | ------------------------------------------------- |
| zt | string | 10-digit hex ZeroTier address |
| etherType | integer | Ethernet frame type |
| mac | string | Hex MAC address (with or without :'s) |
| ip | string | IPv4 or IPv6 address |
| ipTos | integer | IP type of service |
| ipProtocol | integer | IP protocol (e.g. TCP) |
| start | integer | Start of an integer range (e.g. port range) |
| end | integer | End of an integer range (inclusive) |
| id | integer | Tag ID |
| value | integer | Tag value or comparison value |
| mask | integer | Bit mask (for characteristics flags) |
The entry types and their additional fields are:
| Entry type | Description | Fields |
| ------------------------------- | ----------------------------------------------------------------- | -------------- |
| `ACTION_DROP` | Drop any packets matching this rule | (none) |
| `ACTION_ACCEPT` | Accept any packets matching this rule | (none) |
| `ACTION_TEE` | Send a copy of this packet to a node (rule parsing continues) | `zt` |
| `ACTION_REDIRECT` | Redirect this packet to another node | `zt` |
| `ACTION_DEBUG_LOG` | Output debug info on match (if built with rules engine debug) | (none) |
| `MATCH_SOURCE_ZEROTIER_ADDRESS` | Match VL1 ZeroTier address of packet sender. | `zt` |
| `MATCH_DEST_ZEROTIER_ADDRESS` | Match VL1 ZeroTier address of recipient | `zt` |
| `MATCH_ETHERTYPE` | Match Ethernet frame type | `etherType` |
| `MATCH_MAC_SOURCE` | Match source Ethernet MAC address | `mac` |
| `MATCH_MAC_DEST` | Match destination Ethernet MAC address | `mac` |
| `MATCH_IPV4_SOURCE` | Match source IPv4 address | `ip` |
| `MATCH_IPV4_DEST` | Match destination IPv4 address | `ip` |
| `MATCH_IPV6_SOURCE` | Match source IPv6 address | `ip` |
| `MATCH_IPV6_DEST` | Match destination IPv6 address | `ip` |
| `MATCH_IP_TOS` | Match IP TOS field | `ipTos` |
| `MATCH_IP_PROTOCOL` | Match IP protocol field | `ipProtocol` |
| `MATCH_IP_SOURCE_PORT_RANGE` | Match a source IP port range | `start`,`end` |
| `MATCH_IP_DEST_PORT_RANGE` | Match a destination IP port range | `start`,`end` |
| `MATCH_CHARACTERISTICS` | Match on characteristics flags | `mask`,`value` |
| `MATCH_FRAME_SIZE_RANGE` | Match a range of Ethernet frame sizes | `start`,`end` |
| `MATCH_TAGS_SAMENESS` | Match if both sides' tags differ by no more than value | `id`,`value` |
| `MATCH_TAGS_BITWISE_AND` | Match if both sides' tags AND to value | `id`,`value` |
| `MATCH_TAGS_BITWISE_OR` | Match if both sides' tags OR to value | `id`,`value` |
| `MATCH_TAGS_BITWISE_XOR` | Match if both sides` tags XOR to value | `id`,`value` |
Important notes about rules engine behavior:
* IPv4 and IPv6 IP address rules do not match for frames that are not IPv4 or IPv6 respectively.
* `ACTION_DEBUG_LOG` is a no-op on nodes not built with `ZT_RULES_ENGINE_DEBUGGING` enabled (see Network.cpp). If that is enabled nodes will dump a trace of rule evaluation results to *stdout* when this action is encountered but will otherwise keep evaluating rules. This is used for basic "smoke testing" of the rules engine.
* Multicast packets and packets destined for bridged devices treated a little differently. They are matched more than once. They are matched at the point of send with a NULL ZeroTier destination address, meaning that `MATCH_DEST_ZEROTIER_ADDRESS` is useless. That's because the true VL1 destination is not yet known. Then they are matched again for each true VL1 destination. On these later subsequent matches TEE actions are ignored and REDIRECT rules are interpreted as DROPs. This prevents multiple TEE or REDIRECT packets from being sent to third party devices.
* Rules in capabilities are always matched as if the current device is the sender (inbound == false). A capability specifies sender side rules that can be enforced on both sides.
#### `/controller/network/<network ID>/member`
* Purpose: Get a set of all members on this network
* Methods: GET
* Returns: { object }
This returns a JSON object containing all member IDs as keys and their `memberRevisionCounter` values as values.
#### `/controller/network/<network ID>/active`
* Purpose: Get a set of all active members on this network
* Methods: GET
* Returns: { object }
This returns an object containing all currently online members and the most recent `recentLog` entries for their last request.
#### `/controller/network/<network ID>/member/<address>`
* Purpose: Create, authorize, or remove a network member
* Methods: GET, POST, DELETE
* Returns: { object }
| Field | Type | Description | Writable |
| --------------------- | ------------- | ------------------------------------------------- | -------- |
| id | string | Member's 10-digit ZeroTier address | no |
| address | string | Member's 10-digit ZeroTier address | no |
| nwid | string | 16-digit network ID | no |
| clock | integer | Current clock, ms since epoch | no |
| authorized | boolean | Is member authorized? (for private networks) | YES |
| authHistory | array[object] | History of auth changes, latest at end | no |
| activeBridge | boolean | Member is able to bridge to other Ethernet nets | YES |
| identity | string | Member's public ZeroTier identity (if known) | no |
| ipAssignments | array[string] | Managed IP address assignments | YES |
| memberRevision | integer | Member revision counter | no |
| recentLog | array[object] | Recent member activity log; see below | no |
Note that managed IP assignments are only used if they fall within a managed route. Otherwise they are ignored.
**Recent log object format:**
| Field | Type | Description |
| --------------------- | ------------- | ------------------------------------------------- |
| ts | integer | Time of request, ms since epoch |
| authorized | boolean | Was member authorized? |
| clientMajorVersion | integer | Client major version or -1 if unknown |
| clientMinorVersion | integer | Client minor version or -1 if unknown |
| clientRevision | integer | Client revision or -1 if unknown |
| clientProtocolVersion | integer | ZeroTier protocol version reported by client |
| fromAddr | string | Physical address if known |
The controller can only know a member's `fromAddr` if it's able to establish a direct path to it. Members behind very restrictive firewalls may not have this information since the controller will be receiving the member's requests by way of a relay. ZeroTier does not back-trace IP paths as packets are relayed since this would add a lot of protocol overhead.

View File

@@ -1,10 +1 @@
Miscellaneous Stuff The ext/ folder contains third party code, drivers, installation support files, etc.
======
This subfolder contains:
* Bundled third party libraries that are compiled into the binary on platforms and Linux distributions where they are not available on the system.
* Pre-compiled binaries for some platforms, such as pre-built and signed drivers for Mac and Windows.
* Miscellaneous files used by installers and packages on various platform targets.

View File

@@ -61,8 +61,3 @@ Marc O'Morain <github.com@marcomorain.com>
Jeff Pinner <jpinner@twitter.com> Jeff Pinner <jpinner@twitter.com>
Timothy J Fontaine <tjfontaine@gmail.com> Timothy J Fontaine <tjfontaine@gmail.com>
Akagi201 <akagi201@gmail.com> Akagi201 <akagi201@gmail.com>
Romain Giraud <giraud.romain@gmail.com>
Jay Satiro <raysatiro@yahoo.com>
Arne Steen <Arne.Steen@gmx.de>
Kjell Schubert <kjell.schubert@gmail.com>
Olivier Mengué <dolmen@cpan.org>

View File

@@ -440,7 +440,7 @@ enum http_host_state
* character or %x80-FF * character or %x80-FF
**/ **/
#define IS_HEADER_CHAR(ch) \ #define IS_HEADER_CHAR(ch) \
(ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127)) (ch == CR || ch == LF || ch == 9 || (ch > 31 && ch != 127))
#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) #define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
@@ -1007,40 +1007,89 @@ reexecute:
UPDATE_STATE(s_req_spaces_before_url); UPDATE_STATE(s_req_spaces_before_url);
} else if (ch == matcher[parser->index]) { } else if (ch == matcher[parser->index]) {
; /* nada */ ; /* nada */
} else if (IS_ALPHA(ch)) { } else if (parser->method == HTTP_CONNECT) {
if (parser->index == 1 && ch == 'H') {
switch (parser->method << 16 | parser->index << 8 | ch) { parser->method = HTTP_CHECKOUT;
#define XX(meth, pos, ch, new_meth) \ } else if (parser->index == 2 && ch == 'P') {
case (HTTP_##meth << 16 | pos << 8 | ch): \ parser->method = HTTP_COPY;
parser->method = HTTP_##new_meth; break; } else {
XX(POST, 1, 'U', PUT)
XX(POST, 1, 'A', PATCH)
XX(CONNECT, 1, 'H', CHECKOUT)
XX(CONNECT, 2, 'P', COPY)
XX(MKCOL, 1, 'O', MOVE)
XX(MKCOL, 1, 'E', MERGE)
XX(MKCOL, 2, 'A', MKACTIVITY)
XX(MKCOL, 3, 'A', MKCALENDAR)
XX(SUBSCRIBE, 1, 'E', SEARCH)
XX(REPORT, 2, 'B', REBIND)
XX(POST, 1, 'R', PROPFIND)
XX(PROPFIND, 4, 'P', PROPPATCH)
XX(PUT, 2, 'R', PURGE)
XX(LOCK, 1, 'I', LINK)
XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
XX(UNLOCK, 2, 'B', UNBIND)
XX(UNLOCK, 3, 'I', UNLINK)
#undef XX
default:
SET_ERRNO(HPE_INVALID_METHOD); SET_ERRNO(HPE_INVALID_METHOD);
goto error; goto error;
} }
} else if (ch == '-' && } else if (parser->method == HTTP_MKCOL) {
parser->index == 1 && if (parser->index == 1 && ch == 'O') {
parser->method == HTTP_MKCOL) { parser->method = HTTP_MOVE;
} else if (parser->index == 1 && ch == 'E') {
parser->method = HTTP_MERGE;
} else if (parser->index == 1 && ch == '-') {
parser->method = HTTP_MSEARCH; parser->method = HTTP_MSEARCH;
} else if (parser->index == 2 && ch == 'A') {
parser->method = HTTP_MKACTIVITY;
} else if (parser->index == 3 && ch == 'A') {
parser->method = HTTP_MKCALENDAR;
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else if (parser->method == HTTP_SUBSCRIBE) {
if (parser->index == 1 && ch == 'E') {
parser->method = HTTP_SEARCH;
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else if (parser->method == HTTP_REPORT) {
if (parser->index == 2 && ch == 'B') {
parser->method = HTTP_REBIND;
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else if (parser->index == 1) {
if (parser->method == HTTP_POST) {
if (ch == 'R') {
parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
} else if (ch == 'U') {
parser->method = HTTP_PUT; /* or HTTP_PURGE */
} else if (ch == 'A') {
parser->method = HTTP_PATCH;
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else if (parser->method == HTTP_LOCK) {
if (ch == 'I') {
parser->method = HTTP_LINK;
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
}
} else if (parser->index == 2) {
if (parser->method == HTTP_PUT) {
if (ch == 'R') {
parser->method = HTTP_PURGE;
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else if (parser->method == HTTP_UNLOCK) {
if (ch == 'S') {
parser->method = HTTP_UNSUBSCRIBE;
} else if(ch == 'B') {
parser->method = HTTP_UNBIND;
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
parser->method = HTTP_PROPPATCH;
} else if (parser->index == 3 && parser->method == HTTP_UNLOCK && ch == 'I') {
parser->method = HTTP_UNLINK;
} else { } else {
SET_ERRNO(HPE_INVALID_METHOD); SET_ERRNO(HPE_INVALID_METHOD);
goto error; goto error;
@@ -1812,9 +1861,6 @@ reexecute:
case 0: case 0:
break; break;
case 2:
parser->upgrade = 1;
case 1: case 1:
parser->flags |= F_SKIPBODY; parser->flags |= F_SKIPBODY;
break; break;
@@ -2253,13 +2299,12 @@ http_parse_host_char(enum http_host_state s, const char ch) {
static int static int
http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
//assert(u->field_set & (1 << UF_HOST));
enum http_host_state s; enum http_host_state s;
const char *p; const char *p;
size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
assert(u->field_set & (1 << UF_HOST));
u->field_data[UF_HOST].len = 0; u->field_data[UF_HOST].len = 0;
s = found_at ? s_http_userinfo_start : s_http_host_start; s = found_at ? s_http_userinfo_start : s_http_host_start;

View File

@@ -26,8 +26,8 @@ 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 6
#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__) && \
@@ -77,11 +77,6 @@ typedef struct http_parser_settings http_parser_settings;
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body. * chunked' headers that indicate the presence of a body.
* *
* Returning `2` from on_headers_complete will tell parser that it should not
* expect neither a body nor any futher responses on this connection. This is
* useful for handling responses to a CONNECT request which may not contain
* `Upgrade` or `Connection: upgrade` headers.
*
* http_data_cb does not return data chunks. It will be called arbitrarily * http_data_cb does not return data chunks. It will be called arbitrarily
* many times for each string. E.G. you might get 10 callbacks for "on_url" * many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data. * each providing just a few characters more data.

View File

@@ -37,7 +37,7 @@
<key>GID</key> <key>GID</key>
<integer>80</integer> <integer>80</integer>
<key>PATH</key> <key>PATH</key>
<string>mac-ui-macgap1-wrapper/bin/ZeroTier One.app</string> <string>../../mac-ui-macgap1-wrapper/bin/ZeroTier One.app</string>
<key>PATH_TYPE</key> <key>PATH_TYPE</key>
<integer>1</integer> <integer>1</integer>
<key>PERMISSIONS</key> <key>PERMISSIONS</key>
@@ -130,7 +130,7 @@
<key>GID</key> <key>GID</key>
<integer>0</integer> <integer>0</integer>
<key>PATH</key> <key>PATH</key>
<string>ui/index.html</string> <string>../../../ui/index.html</string>
<key>PATH_TYPE</key> <key>PATH_TYPE</key>
<integer>1</integer> <integer>1</integer>
<key>PERMISSIONS</key> <key>PERMISSIONS</key>
@@ -146,7 +146,7 @@
<key>GID</key> <key>GID</key>
<integer>0</integer> <integer>0</integer>
<key>PATH</key> <key>PATH</key>
<string>ui/main.js</string> <string>../../../ui/main.js</string>
<key>PATH_TYPE</key> <key>PATH_TYPE</key>
<integer>1</integer> <integer>1</integer>
<key>PERMISSIONS</key> <key>PERMISSIONS</key>
@@ -162,7 +162,7 @@
<key>GID</key> <key>GID</key>
<integer>0</integer> <integer>0</integer>
<key>PATH</key> <key>PATH</key>
<string>ui/react.min.js</string> <string>../../../ui/react.min.js</string>
<key>PATH_TYPE</key> <key>PATH_TYPE</key>
<integer>1</integer> <integer>1</integer>
<key>PERMISSIONS</key> <key>PERMISSIONS</key>
@@ -178,7 +178,7 @@
<key>GID</key> <key>GID</key>
<integer>0</integer> <integer>0</integer>
<key>PATH</key> <key>PATH</key>
<string>ui/simpleajax.min.js</string> <string>../../../ui/simpleajax.min.js</string>
<key>PATH_TYPE</key> <key>PATH_TYPE</key>
<integer>1</integer> <integer>1</integer>
<key>PERMISSIONS</key> <key>PERMISSIONS</key>
@@ -194,7 +194,7 @@
<key>GID</key> <key>GID</key>
<integer>0</integer> <integer>0</integer>
<key>PATH</key> <key>PATH</key>
<string>ui/zerotier.css</string> <string>../../../ui/zerotier.css</string>
<key>PATH_TYPE</key> <key>PATH_TYPE</key>
<integer>1</integer> <integer>1</integer>
<key>PERMISSIONS</key> <key>PERMISSIONS</key>
@@ -210,7 +210,7 @@
<key>GID</key> <key>GID</key>
<integer>0</integer> <integer>0</integer>
<key>PATH</key> <key>PATH</key>
<string>ui/ztui.min.js</string> <string>../../../ui/ztui.min.js</string>
<key>PATH_TYPE</key> <key>PATH_TYPE</key>
<integer>1</integer> <integer>1</integer>
<key>PERMISSIONS</key> <key>PERMISSIONS</key>
@@ -759,7 +759,7 @@
<key>OVERWRITE_PERMISSIONS</key> <key>OVERWRITE_PERMISSIONS</key>
<false/> <false/>
<key>VERSION</key> <key>VERSION</key>
<string>1.1.14</string> <string>1.0.3</string>
</dict> </dict>
<key>PROJECT_COMMENTS</key> <key>PROJECT_COMMENTS</key>
<dict> <dict>
@@ -773,27 +773,26 @@
ZW50LVN0eWxlLVR5cGUiIGNvbnRlbnQ9InRleHQvY3NzIj4KPHRp ZW50LVN0eWxlLVR5cGUiIGNvbnRlbnQ9InRleHQvY3NzIj4KPHRp
dGxlPjwvdGl0bGU+CjxtZXRhIG5hbWU9IkdlbmVyYXRvciIgY29u dGxlPjwvdGl0bGU+CjxtZXRhIG5hbWU9IkdlbmVyYXRvciIgY29u
dGVudD0iQ29jb2EgSFRNTCBXcml0ZXIiPgo8bWV0YSBuYW1lPSJD dGVudD0iQ29jb2EgSFRNTCBXcml0ZXIiPgo8bWV0YSBuYW1lPSJD
b2NvYVZlcnNpb24iIGNvbnRlbnQ9IjE0MDQuNDciPgo8c3R5bGUg b2NvYVZlcnNpb24iIGNvbnRlbnQ9IjEzNDcuNTciPgo8c3R5bGUg
dHlwZT0idGV4dC9jc3MiPgpwLnAxIHttYXJnaW46IDAuMHB4IDAu dHlwZT0idGV4dC9jc3MiPgpwLnAxIHttYXJnaW46IDAuMHB4IDAu
MHB4IDAuMHB4IDAuMHB4OyBsaW5lLWhlaWdodDogMTQuMHB4OyBm MHB4IDAuMHB4IDAuMHB4OyBmb250OiAxMi4wcHggSGVsdmV0aWNh
b250OiAxMi4wcHggSGVsdmV0aWNhOyBjb2xvcjogIzAwMDAwMDsg OyBjb2xvcjogIzAwMDAwMDsgLXdlYmtpdC10ZXh0LXN0cm9rZTog
LXdlYmtpdC10ZXh0LXN0cm9rZTogIzAwMDAwMH0Kc3Bhbi5zMSB7 IzAwMDAwMH0Kc3Bhbi5zMSB7Zm9udC1rZXJuaW5nOiBub25lfQo8
Zm9udC1rZXJuaW5nOiBub25lfQo8L3N0eWxlPgo8L2hlYWQ+Cjxi L3N0eWxlPgo8L2hlYWQ+Cjxib2R5Pgo8cCBjbGFzcz0icDEiPjxz
b2R5Pgo8cCBjbGFzcz0icDEiPjxzcGFuIGNsYXNzPSJzMSI+WmVy cGFuIGNsYXNzPSJzMSI+WmVyb1RpZXIgT25lIC0gTmV0d29yayBW
b1RpZXIgT25lIC0gTmV0d29yayBWaXJ0dWFsaXphdGlvbiBFdmVy aXJ0dWFsaXphdGlvbiBFdmVyeXdoZXJlPC9zcGFuPjwvcD4KPHAg
eXdoZXJlPC9zcGFuPjwvcD4KPHAgY2xhc3M9InAxIj48c3BhbiBj Y2xhc3M9InAxIj48c3BhbiBjbGFzcz0iczEiPihjKTIwMTEtMjAx
bGFzcz0iczEiPihjKTIwMTEtMjAxNiBaZXJvVGllciwgSW5jLjwv NSBaZXJvVGllciwgSW5jLjwvc3Bhbj48L3A+CjxwIGNsYXNzPSJw
c3Bhbj48L3A+CjxwIGNsYXNzPSJwMSI+PHNwYW4gY2xhc3M9InMx MSI+PHNwYW4gY2xhc3M9InMxIj5jb250YWN0QHplcm90aWVyLmNv
Ij5jb250YWN0QHplcm90aWVyLmNvbTwvc3Bhbj48L3A+CjxwIGNs bTwvc3Bhbj48L3A+CjxwIGNsYXNzPSJwMSI+PHNwYW4gY2xhc3M9
YXNzPSJwMSI+PHNwYW4gY2xhc3M9InMxIj48YnI+Cjwvc3Bhbj48 InMxIj48YnI+Cjwvc3Bhbj48L3A+CjxwIGNsYXNzPSJwMSI+PHNw
L3A+CjxwIGNsYXNzPSJwMSI+PHNwYW4gY2xhc3M9InMxIj5UbyB1 YW4gY2xhc3M9InMxIj5UbyB1bmluc3RhbGwgbWFudWFsbHksIHR5
bmluc3RhbGwgbWFudWFsbHksIHR5cGUgdGhlIGZvbGxvd2luZyBp cGUgdGhlIGZvbGxvd2luZyBpbiBhIHRlcm1pbmFsIHdpbmRvdzo8
biBhIHRlcm1pbmFsIHdpbmRvdzo8L3NwYW4+PC9wPgo8cCBjbGFz L3NwYW4+PC9wPgo8cCBjbGFzcz0icDEiPjxzcGFuIGNsYXNzPSJz
cz0icDEiPjxzcGFuIGNsYXNzPSJzMSI+PGJyPgo8L3NwYW4+PC9w MSI+PGJyPgo8L3NwYW4+PC9wPgo8cCBjbGFzcz0icDEiPjxzcGFu
Pgo8cCBjbGFzcz0icDEiPjxzcGFuIGNsYXNzPSJzMSI+c3VkbyAi IGNsYXNzPSJzMSI+c3VkbyAiL0xpYnJhcnkvQXBwbGljYXRpb24g
L0xpYnJhcnkvQXBwbGljYXRpb24gU3VwcG9ydC9aZXJvVGllci9P U3VwcG9ydC9aZXJvVGllci9PbmUvdW5pbnN0YWxsLnNoIjwvc3Bh
bmUvdW5pbnN0YWxsLnNoIjwvc3Bhbj48L3A+CjwvYm9keT4KPC9o bj48L3A+CjwvYm9keT4KPC9odG1sPgo=
dG1sPgo=
</data> </data>
</dict> </dict>
<key>PROJECT_SETTINGS</key> <key>PROJECT_SETTINGS</key>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DOCUMENT Type="Advanced Installer" CreateVersion="10.9" version="12.5.1" Modules="enterprise" RootPath="." Language="en" Id="{DC564647-6BF0-4550-87F4-89C938D0159C}"> <DOCUMENT Type="Advanced Installer" CreateVersion="10.9" version="12.5.1" Modules="enterprise" RootPath="." Language="en" Id="{DC564647-6BF0-4550-87F4-89C938D0159C}">
<COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent"> <COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent">
<ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;MsiDriverPackagesComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent;MsiAppSearchComponent"/> <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;MsiDriverPackagesComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;MsiEnvComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent;MsiAppSearchComponent"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent"> <COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
<ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/> <ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
@@ -26,10 +26,10 @@
<ROW Property="CTRLS" Value="2"/> <ROW Property="CTRLS" Value="2"/>
<ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/> <ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
<ROW Property="Manufacturer" Value="ZeroTier, Inc."/> <ROW Property="Manufacturer" Value="ZeroTier, Inc."/>
<ROW Property="ProductCode" Value="1033:{5E48BAF3-4126-452A-884D-ED734D22CD02} " Type="16"/> <ROW Property="ProductCode" Value="1033:{A6D97FB1-02FA-4042-A0EE-A080D53CDBBF} " Type="16"/>
<ROW Property="ProductLanguage" Value="1033"/> <ROW Property="ProductLanguage" Value="1033"/>
<ROW Property="ProductName" Value="ZeroTier One"/> <ROW Property="ProductName" Value="ZeroTier One"/>
<ROW Property="ProductVersion" Value="1.1.14" Type="32"/> <ROW Property="ProductVersion" Value="1.1.5" Type="32"/>
<ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/> <ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
<ROW Property="RUNAPPLICATION" Value="1" Type="4"/> <ROW Property="RUNAPPLICATION" Value="1" Type="4"/>
<ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND"/> <ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND"/>
@@ -54,15 +54,17 @@
<ROW Directory="networks.d_Dir" Directory_Parent="One_Dir" DefaultDir="networks.d"/> <ROW Directory="networks.d_Dir" Directory_Parent="One_Dir" DefaultDir="networks.d"/>
<ROW Directory="regid.201001.com.zerotier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="REGID2~1.ZER|regid.2010-01.com.zerotier"/> <ROW Directory="regid.201001.com.zerotier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="REGID2~1.ZER|regid.2010-01.com.zerotier"/>
<ROW Directory="tapwindows_Dir" Directory_Parent="One_Dir" DefaultDir="TAP-WI~1|tap-windows"/> <ROW Directory="tapwindows_Dir" Directory_Parent="One_Dir" DefaultDir="TAP-WI~1|tap-windows"/>
<ROW Directory="ui_Dir" Directory_Parent="One_Dir" DefaultDir="ui" DirectoryOptions="3"/>
<ROW Directory="x64_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x64"/> <ROW Directory="x64_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x64"/>
<ROW Directory="x86_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x86"/> <ROW Directory="x86_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x86"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent"> <COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
<ROW Component="AI_CustomARPName" ComponentId="{AE82C83F-959D-492C-A1B8-B3F741D22D7E}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/> <ROW Component="AI_CustomARPName" ComponentId="{738BDE1C-E12F-4DFB-B279-9038EECEFF45}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
<ROW Component="AI_DisableModify" ComponentId="{020DCABD-5D56-49B9-AF48-F07F0B55E590}" Directory_="APPDIR" Attributes="4" KeyPath="NoModify" Options="1"/> <ROW Component="AI_DisableModify" ComponentId="{020DCABD-5D56-49B9-AF48-F07F0B55E590}" Directory_="APPDIR" Attributes="4" KeyPath="NoModify" Options="1"/>
<ROW Component="Newtonsoft.Json.dll" ComponentId="{0B2F229D-5425-42FB-9E28-F6D25AB2B4B5}" Directory_="APPDIR" Attributes="0" KeyPath="Newtonsoft.Json.dll"/> <ROW Component="Newtonsoft.Json.dll" ComponentId="{0B2F229D-5425-42FB-9E28-F6D25AB2B4B5}" Directory_="APPDIR" Attributes="0" KeyPath="Newtonsoft.Json.dll"/>
<ROW Component="ProductInformation" ComponentId="{DB078D04-EA8E-4A7C-9001-89BAD932F9D9}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/> <ROW Component="ProductInformation" ComponentId="{DB078D04-EA8E-4A7C-9001-89BAD932F9D9}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
<ROW Component="ZeroTierOne.exe" ComponentId="{18B51525-77BF-4FD9-9C18-A10D4CFC25BA}" Directory_="APPDIR" Attributes="0" KeyPath="ZeroTierOne.exe"/> <ROW Component="ZeroTierOne.exe" ComponentId="{18B51525-77BF-4FD9-9C18-A10D4CFC25BA}" Directory_="APPDIR" Attributes="0" KeyPath="ZeroTierOne.exe"/>
<ROW Component="index.html" ComponentId="{24AB46DC-56EA-4F3C-A8B7-95957509CDD1}" Directory_="ui_Dir" Attributes="0" KeyPath="index.html" Type="0"/>
<ROW Component="networks.d" ComponentId="{EF54D0DF-889F-41DC-AF5C-4E7F96AB1C8B}" Directory_="networks.d_Dir" Attributes="0"/> <ROW Component="networks.d" ComponentId="{EF54D0DF-889F-41DC-AF5C-4E7F96AB1C8B}" Directory_="networks.d_Dir" Attributes="0"/>
<ROW Component="regid.201001.com.zerotier" ComponentId="{A39C80FC-6A8F-454F-9052-10DAC3C3B139}" Directory_="regid.201001.com.zerotier_Dir" Attributes="0"/> <ROW Component="regid.201001.com.zerotier" ComponentId="{A39C80FC-6A8F-454F-9052-10DAC3C3B139}" Directory_="regid.201001.com.zerotier_Dir" Attributes="0"/>
<ROW Component="zerotierone_x64.exe" ComponentId="{DFCFB72D-B055-4E60-B6D8-81FF585C2183}" Directory_="One_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zerotierone_x64.exe"/> <ROW Component="zerotierone_x64.exe" ComponentId="{DFCFB72D-B055-4E60-B6D8-81FF585C2183}" Directory_="One_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zerotierone_x64.exe"/>
@@ -71,20 +73,26 @@
<ROW Component="zttap300.cat_1" ComponentId="{9F913E48-095B-4EA3-98DA-EDAB1593F3E3}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zttap300.cat_3" Type="0"/> <ROW Component="zttap300.cat_1" ComponentId="{9F913E48-095B-4EA3-98DA-EDAB1593F3E3}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zttap300.cat_3" Type="0"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent"> <COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
<ROW Feature="ZeroTierOne" Title="MainFeature" Description="ZeroTier One" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName AI_DisableModify Newtonsoft.Json.dll ProductInformation ZeroTierOne.exe networks.d regid.201001.com.zerotier zerotierone_x64.exe zerotierone_x86.exe zttap300.cat zttap300.cat_1"/> <ROW Feature="ZeroTierOne" Title="MainFeature" Description="ZeroTier One" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName AI_DisableModify Newtonsoft.Json.dll ProductInformation ZeroTierOne.exe index.html networks.d regid.201001.com.zerotier zerotierone_x64.exe zerotierone_x86.exe zttap300.cat zttap300.cat_1"/>
<ATTRIBUTE name="CurrentFeature" value="ZeroTierOne"/> <ATTRIBUTE name="CurrentFeature" value="ZeroTierOne"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent"> <COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
<ROW File="Newtonsoft.Json.dll" Component_="Newtonsoft.Json.dll" FileName="NEWTON~1.DLL|Newtonsoft.Json.dll" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\Newtonsoft.Json.dll" SelfReg="false" DigSign="true"/> <ROW File="Newtonsoft.Json.dll" Component_="Newtonsoft.Json.dll" FileName="NEWTON~1.DLL|Newtonsoft.Json.dll" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\Newtonsoft.Json.dll" SelfReg="false" DigSign="true"/>
<ROW File="ZeroTierOne.exe" Component_="ZeroTierOne.exe" FileName="ZEROTI~1.EXE|ZeroTier One.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\ZeroTier One.exe" SelfReg="false" NextFile="zttap300.cat_2" DigSign="true"/> <ROW File="ZeroTierOne.exe" Component_="ZeroTierOne.exe" FileName="ZEROTI~1.EXE|ZeroTier One.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\ZeroTier One.exe" SelfReg="false" NextFile="zttap300.cat_2" DigSign="true"/>
<ROW File="index.html" Component_="index.html" FileName="INDEX~1.HTM|index.html" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\ui\index.html" SelfReg="false" NextFile="main.js"/>
<ROW File="main.js" Component_="index.html" FileName="main.js" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\ui\main.js" SelfReg="false" NextFile="react.min.js"/>
<ROW File="react.min.js" Component_="index.html" FileName="REACTM~1.JS|react.min.js" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\ui\react.min.js" SelfReg="false" NextFile="simpleajax.min.js"/>
<ROW File="simpleajax.min.js" Component_="index.html" FileName="SIMPLE~1.JS|simpleajax.min.js" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\ui\simpleajax.min.js" SelfReg="false" NextFile="zerotier.css"/>
<ROW File="zerotier.css" Component_="index.html" FileName="zerotier.css" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\ui\zerotier.css" SelfReg="false" NextFile="ztui.min.js"/>
<ROW File="zerotierone_x64.exe" Component_="zerotierone_x64.exe" FileName="ZEROTI~2.EXE|zerotier-one_x64.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\x64\Release\zerotier-one_x64.exe" SelfReg="false" NextFile="ZeroTierOne.exe" DigSign="true"/> <ROW File="zerotierone_x64.exe" Component_="zerotierone_x64.exe" FileName="ZEROTI~2.EXE|zerotier-one_x64.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\x64\Release\zerotier-one_x64.exe" SelfReg="false" NextFile="ZeroTierOne.exe" DigSign="true"/>
<ROW File="zerotierone_x86.exe" Component_="zerotierone_x86.exe" FileName="ZEROTI~1.EXE|zerotier-one_x86.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\Win32\Release\zerotier-one_x86.exe" SelfReg="false" NextFile="zerotierone_x64.exe" DigSign="true"/> <ROW File="zerotierone_x86.exe" Component_="zerotierone_x86.exe" FileName="ZEROTI~1.EXE|zerotier-one_x86.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\Win32\Release\zerotier-one_x86.exe" SelfReg="false" NextFile="zerotierone_x64.exe" DigSign="true"/>
<ROW File="zttap300.cat_2" Component_="zttap300.cat" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_2"/> <ROW File="zttap300.cat_2" Component_="zttap300.cat" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_2"/>
<ROW File="zttap300.cat_3" Component_="zttap300.cat_1" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_3"/> <ROW File="zttap300.cat_3" Component_="zttap300.cat_1" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_3"/>
<ROW File="zttap300.inf" Component_="zttap300.cat" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.inf" SelfReg="false" NextFile="zttap300.cat_3"/> <ROW File="zttap300.inf" Component_="zttap300.cat" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.inf" SelfReg="false" NextFile="zttap300.cat_3"/>
<ROW File="zttap300.inf_1" Component_="zttap300.cat_1" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.inf" SelfReg="false" NextFile="Newtonsoft.Json.dll"/> <ROW File="zttap300.inf_1" Component_="zttap300.cat_1" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.inf" SelfReg="false" NextFile="index.html"/>
<ROW File="zttap300.sys_2" Component_="zttap300.cat" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.sys" SelfReg="false" NextFile="zttap300.inf"/> <ROW File="zttap300.sys_2" Component_="zttap300.cat" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.sys" SelfReg="false" NextFile="zttap300.inf"/>
<ROW File="zttap300.sys_3" Component_="zttap300.cat_1" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.sys" SelfReg="false" NextFile="zttap300.inf_1"/> <ROW File="zttap300.sys_3" Component_="zttap300.cat_1" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.sys" SelfReg="false" NextFile="zttap300.inf_1"/>
<ROW File="ztui.min.js" Component_="index.html" FileName="ZTUIMI~1.JS|ztui.min.js" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\ui\ztui.min.js" SelfReg="false" NextFile="Newtonsoft.Json.dll"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.BuildComponent"> <COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
<ROW BuildKey="DefaultBuild" BuildName="MSI" BuildOrder="1" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="4" ExtUI="true" UseLargeSchema="true"/> <ROW BuildKey="DefaultBuild" BuildName="MSI" BuildOrder="1" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="4" ExtUI="true" UseLargeSchema="true"/>
@@ -106,7 +114,7 @@
<ROW Path="&lt;AI_DICTS&gt;ui_en.ail"/> <ROW Path="&lt;AI_DICTS&gt;ui_en.ail"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.DigCertStoreComponent"> <COMPONENT cid="caphyon.advinst.msicomp.DigCertStoreComponent">
<ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="ZeroTier One" DescriptionUrl="https://www.zerotier.com/" SignOptions="7" SignTool="0" UseSha256="1" Thumbprint="45f6b8ef02b543eeaa08d5f0f08ebe72c2a8a2d5 Subject: ZeroTier, Inc.&#10;Issuer: DigiCert SHA2 High Assurance Code Signing CA&#10;Valid from 03/22/2016 to 05/06/2019"/> <ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="ZeroTier One" DescriptionUrl="https://www.zerotier.com/" SignOptions="7" SignTool="0" Thumbprint="2ad023dc7aa92bf4265b33852a2ed2406d2bee86 Subject: ZeroTier Networks LLC&#10;Issuer: DigiCert High Assurance Code Signing CA-1&#10;Valid from 04/24/2015 to 04/01/2016"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.FirewallExceptionComponent"> <COMPONENT cid="caphyon.advinst.msicomp.FirewallExceptionComponent">
<ROW FirewallException="ZeroTierOneUDP9993" DisplayName="ZeroTier One UDP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/> <ROW FirewallException="ZeroTierOneUDP9993" DisplayName="ZeroTier One UDP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/>
@@ -130,10 +138,6 @@
<ROW Action="AI_FwRemove" Description="Executing Windows Firewall configurations" Template="Configuring Windows Firewall rule: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_FwRemove" TemplateLocId="ActionText.Template.AI_FwRemove"/> <ROW Action="AI_FwRemove" Description="Executing Windows Firewall configurations" Template="Configuring Windows Firewall rule: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_FwRemove" TemplateLocId="ActionText.Template.AI_FwRemove"/>
<ROW Action="AI_FwRollback" Description="Rolling back Windows Firewall configurations." Template="Rolling back Windows Firewall configurations." DescriptionLocId="ActionText.Description.AI_FwRollback" TemplateLocId="ActionText.Template.AI_FwRollback"/> <ROW Action="AI_FwRollback" Description="Rolling back Windows Firewall configurations." Template="Rolling back Windows Firewall configurations." DescriptionLocId="ActionText.Description.AI_FwRollback" TemplateLocId="ActionText.Template.AI_FwRollback"/>
<ROW Action="AI_FwUninstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwUninstall"/> <ROW Action="AI_FwUninstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwUninstall"/>
<ROW Action="AI_TxtUpdaterCommit" Description="Commit text file changes. " Template="Commit text file changes." DescriptionLocId="ActionText.Description.AI_TxtUpdaterCommit" TemplateLocId="ActionText.Template.AI_TxtUpdaterCommit"/>
<ROW Action="AI_TxtUpdaterConfig" Description="Executing text file updates" Template="Updating text file: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_TxtUpdaterConfig" TemplateLocId="ActionText.Template.AI_TxtUpdaterConfig"/>
<ROW Action="AI_TxtUpdaterInstall" Description="Generating actions to configure text files updates" DescriptionLocId="ActionText.Description.AI_TxtUpdaterInstall"/>
<ROW Action="AI_TxtUpdaterRollback" Description="Rolling back text file changes. " Template="Rolling back text file changes." DescriptionLocId="ActionText.Description.AI_TxtUpdaterRollback" TemplateLocId="ActionText.Template.AI_TxtUpdaterRollback"/>
<ROW Action="AI_XmlCommit" Description="Committing XML file configurations." Template="Committing XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlCommit" TemplateLocId="ActionText.Template.AI_XmlCommit"/> <ROW Action="AI_XmlCommit" Description="Committing XML file configurations." Template="Committing XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlCommit" TemplateLocId="ActionText.Template.AI_XmlCommit"/>
<ROW Action="AI_XmlConfig" Description="Executing XML file configurations" Template="Configuring XML file: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_XmlConfig" TemplateLocId="ActionText.Template.AI_XmlConfig"/> <ROW Action="AI_XmlConfig" Description="Executing XML file configurations" Template="Configuring XML file: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_XmlConfig" TemplateLocId="ActionText.Template.AI_XmlConfig"/>
<ROW Action="AI_XmlInstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlInstall"/> <ROW Action="AI_XmlInstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlInstall"/>
@@ -146,7 +150,6 @@
<ROW Name="NetFirewall.dll" SourcePath="&lt;AI_CUSTACTS&gt;NetFirewall.dll"/> <ROW Name="NetFirewall.dll" SourcePath="&lt;AI_CUSTACTS&gt;NetFirewall.dll"/>
<ROW Name="ShortcutFlags.dll" SourcePath="&lt;AI_CUSTACTS&gt;ShortcutFlags.dll"/> <ROW Name="ShortcutFlags.dll" SourcePath="&lt;AI_CUSTACTS&gt;ShortcutFlags.dll"/>
<ROW Name="SoftwareDetector.dll" SourcePath="&lt;AI_CUSTACTS&gt;SoftwareDetector.dll"/> <ROW Name="SoftwareDetector.dll" SourcePath="&lt;AI_CUSTACTS&gt;SoftwareDetector.dll"/>
<ROW Name="TxtUpdater.dll" SourcePath="&lt;AI_CUSTACTS&gt;TxtUpdater.dll"/>
<ROW Name="aicustact.dll" SourcePath="&lt;AI_CUSTACTS&gt;aicustact.dll"/> <ROW Name="aicustact.dll" SourcePath="&lt;AI_CUSTACTS&gt;aicustact.dll"/>
<ROW Name="chainersupport.dll" SourcePath="&lt;AI_CUSTACTS&gt;chainersupport.dll"/> <ROW Name="chainersupport.dll" SourcePath="&lt;AI_CUSTACTS&gt;chainersupport.dll"/>
<ROW Name="msichainer.exe" SourcePath="&lt;AI_CUSTACTS&gt;msichainer.exe"/> <ROW Name="msichainer.exe" SourcePath="&lt;AI_CUSTACTS&gt;msichainer.exe"/>
@@ -209,8 +212,6 @@
<ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/> <ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
<ROW Action="AI_LaunchApp" Type="1" Source="aicustact.dll" Target="[#ZeroTierOne.exe]"/> <ROW Action="AI_LaunchApp" Type="1" Source="aicustact.dll" Target="[#ZeroTierOne.exe]"/>
<ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/> <ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
<ROW Action="AI_PinShortcuts" Type="1" Source="ShortcutFlags.dll" Target="PinShortcuts"/>
<ROW Action="AI_PinToTaskbar" Type="1025" Source="ShortcutFlags.dll" Target="PinToTaskbar" WithoutSeq="true"/>
<ROW Action="AI_PrepareChainers" Type="1" Source="chainersupport.dll" Target="PrepareChainedPackages"/> <ROW Action="AI_PrepareChainers" Type="1" Source="chainersupport.dll" Target="PrepareChainedPackages"/>
<ROW Action="AI_PrepareShortcutFlags" Type="1" Source="ShortcutFlags.dll" Target="PrepareActionData"/> <ROW Action="AI_PrepareShortcutFlags" Type="1" Source="ShortcutFlags.dll" Target="PrepareActionData"/>
<ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/> <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/>
@@ -220,12 +221,6 @@
<ROW Action="AI_RollbackChainers" Type="11585" Source="chainersupport.dll" Target="RollbackChainedPackages" WithoutSeq="true"/> <ROW Action="AI_RollbackChainers" Type="11585" Source="chainersupport.dll" Target="RollbackChainedPackages" WithoutSeq="true"/>
<ROW Action="AI_SHOW_LOG" Type="65" Source="aicustact.dll" Target="LaunchLogFile" WithoutSeq="true"/> <ROW Action="AI_SHOW_LOG" Type="65" Source="aicustact.dll" Target="LaunchLogFile" WithoutSeq="true"/>
<ROW Action="AI_STORE_LOCATION" Type="51" Source="ARPINSTALLLOCATION" Target="[APPDIR]"/> <ROW Action="AI_STORE_LOCATION" Type="51" Source="ARPINSTALLLOCATION" Target="[APPDIR]"/>
<ROW Action="AI_TxtUpdaterCommit" Type="11777" Source="TxtUpdater.dll" Target="OnTxtUpdaterCommit" WithoutSeq="true"/>
<ROW Action="AI_TxtUpdaterConfig" Type="11265" Source="TxtUpdater.dll" Target="OnTxtUpdaterConfig" WithoutSeq="true"/>
<ROW Action="AI_TxtUpdaterInstall" Type="1" Source="TxtUpdater.dll" Target="OnTxtUpdaterInstall"/>
<ROW Action="AI_TxtUpdaterRollback" Type="11521" Source="TxtUpdater.dll" Target="OnTxtUpdaterRollback" WithoutSeq="true"/>
<ROW Action="AI_UnpinFromTaskbar" Type="1025" Source="ShortcutFlags.dll" Target="UnpinFromTaskbar" WithoutSeq="true"/>
<ROW Action="AI_UnpinShortcuts" Type="1" Source="ShortcutFlags.dll" Target="UnpinShortcuts"/>
<ROW Action="AI_XmlCommit" Type="11777" Source="xmlCfg.dll" Target="OnXmlCommit" WithoutSeq="true"/> <ROW Action="AI_XmlCommit" Type="11777" Source="xmlCfg.dll" Target="OnXmlCommit" WithoutSeq="true"/>
<ROW Action="AI_XmlConfig" Type="11265" Source="xmlCfg.dll" Target="OnXmlConfig" WithoutSeq="true"/> <ROW Action="AI_XmlConfig" Type="11265" Source="xmlCfg.dll" Target="OnXmlConfig" WithoutSeq="true"/>
<ROW Action="AI_XmlInstall" Type="1" Source="xmlCfg.dll" Target="OnXmlInstall" AdditionalSeq="AI_DATA_SETTER"/> <ROW Action="AI_XmlInstall" Type="1" Source="xmlCfg.dll" Target="OnXmlInstall" AdditionalSeq="AI_DATA_SETTER"/>
@@ -241,9 +236,6 @@
<COMPONENT cid="caphyon.advinst.msicomp.MsiEmbeddedChainerComponent"> <COMPONENT cid="caphyon.advinst.msicomp.MsiEmbeddedChainerComponent">
<ROW MsiEmbeddedChainer="msichainer.exe" Condition="VersionMsi &gt;= &quot;4.05&quot;" CommandLine="[AI_CHAINER_CMD_LINE]" Source="msichainer.exe" Type="2"/> <ROW MsiEmbeddedChainer="msichainer.exe" Condition="VersionMsi &gt;= &quot;4.05&quot;" CommandLine="[AI_CHAINER_CMD_LINE]" Source="msichainer.exe" Type="2"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiEnvComponent">
<ROW Environment="Path" Name="=-*Path" Value="[~];[APPDIR]" Component_="ZeroTierOne.exe"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent"> <COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
<ROW Name="ZeroTierIcon.exe" SourcePath="..\..\..\artwork\ZeroTierIcon.ico" Index="0"/> <ROW Name="ZeroTierIcon.exe" SourcePath="..\..\..\artwork\ZeroTierIcon.ico" Index="0"/>
</COMPONENT> </COMPONENT>
@@ -254,8 +246,8 @@
<ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1399"/> <ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1399"/>
<ROW Action="AI_ResolveKnownFolders" Sequence="51"/> <ROW Action="AI_ResolveKnownFolders" Sequence="51"/>
<ROW Action="AI_PrepareShortcutFlags" Condition="(VersionNT &gt; 501) AND ((NOT Installed) OR (Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;)))" Sequence="4501"/> <ROW Action="AI_PrepareShortcutFlags" Condition="(VersionNT &gt; 501) AND ((NOT Installed) OR (Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;)))" Sequence="4501"/>
<ROW Action="AI_XmlInstall" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5103"/> <ROW Action="AI_XmlInstall" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5102"/>
<ROW Action="AI_DATA_SETTER" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5102"/> <ROW Action="AI_DATA_SETTER" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5101"/>
<ROW Action="AI_XmlUninstall" Condition="(REMOVE)" Sequence="3102"/> <ROW Action="AI_XmlUninstall" Condition="(REMOVE)" Sequence="3102"/>
<ROW Action="AI_DATA_SETTER_1" Condition="(REMOVE)" Sequence="3101"/> <ROW Action="AI_DATA_SETTER_1" Condition="(REMOVE)" Sequence="3101"/>
<ROW Action="InstallFinalize" Sequence="6597" SeqType="0" MsiKey="InstallFinalize"/> <ROW Action="InstallFinalize" Sequence="6597" SeqType="0" MsiKey="InstallFinalize"/>
@@ -269,9 +261,6 @@
<ROW Action="AI_FwUninstall" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1702"/> <ROW Action="AI_FwUninstall" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1702"/>
<ROW Action="AI_DATA_SETTER_3" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1701"/> <ROW Action="AI_DATA_SETTER_3" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1701"/>
<ROW Action="AI_DetectSoftware" Sequence="101"/> <ROW Action="AI_DetectSoftware" Sequence="101"/>
<ROW Action="AI_PinShortcuts" Condition="(VersionNT &gt; 600) AND ((NOT Installed) OR (Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;)))" Sequence="4502"/>
<ROW Action="AI_UnpinShortcuts" Condition="(VersionNT &gt; 600) AND (REMOVE = &quot;ALL&quot;)" Sequence="3199"/>
<ROW Action="AI_TxtUpdaterInstall" Sequence="5101"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent"> <COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
<ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/> <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
@@ -342,13 +331,6 @@
<ATTRIBUTE name="LocalFile" value="regid.199509.com.example_ProductName.swidtag"/> <ATTRIBUTE name="LocalFile" value="regid.199509.com.example_ProductName.swidtag"/>
<ATTRIBUTE name="SystemFile" value="regid.199509.com.example_ProductName.swidtag_1"/> <ATTRIBUTE name="SystemFile" value="regid.199509.com.example_ProductName.swidtag_1"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateComponent">
<ROW Name="Append/Create" TxtUpdateSet="zerotiercli.bat" FindPattern="@ECHO OFF&#13;&#10;if [\[][#zerotierone_x64.exe][\]] == [\[][\]] (&#13;&#10;&#9;[#zerotierone_x86.exe] -q %*&#13;&#10;) else (&#13;&#10;&#9;[#zerotierone_x64.exe] -q %*&#13;&#10;)&#13;&#10;" Options="160" Order="0" FileEncoding="0"/>
<ROW Name="Replace" TxtUpdateSet="zerotiercli.bat" FindPattern="YourFindText" ReplacePattern="YourReplaceText" Options="2" Order="1" FileEncoding="-1"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateSetComponent">
<ROW Key="zerotiercli.bat" Component="zerotierone_x64.exe" FileName="zerotier-cli.bat" Directory="APPDIR" Options="17"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.XmlAttributeComponent"> <COMPONENT cid="caphyon.advinst.msicomp.XmlAttributeComponent">
<ROW XmlAttribute="xmlnsds" XmlElement="swidsoftware_identification_tag" Name="xmlns:ds" Flags="14" Order="0" Value="http://www.w3.org/2000/09/xmldsig#"/> <ROW XmlAttribute="xmlnsds" XmlElement="swidsoftware_identification_tag" Name="xmlns:ds" Flags="14" Order="0" Value="http://www.w3.org/2000/09/xmldsig#"/>
<ROW XmlAttribute="xmlnsswid" XmlElement="swidsoftware_identification_tag" Name="xmlns:swid" Flags="14" Order="1" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd"/> <ROW XmlAttribute="xmlnsswid" XmlElement="swidsoftware_identification_tag" Name="xmlns:swid" Flags="14" Order="1" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd"/>
@@ -356,7 +338,7 @@
<ROW XmlAttribute="xsischemaLocation" XmlElement="swidsoftware_identification_tag" Name="xsi:schemaLocation" Flags="14" Order="3" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd software_identification_tag.xsd"/> <ROW XmlAttribute="xsischemaLocation" XmlElement="swidsoftware_identification_tag" Name="xsi:schemaLocation" Flags="14" Order="3" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd software_identification_tag.xsd"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.XmlElementComponent"> <COMPONENT cid="caphyon.advinst.msicomp.XmlElementComponent">
<ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="14"/> <ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="5"/>
<ROW XmlElement="swidentitlement_required_indicator" ParentElement="swidsoftware_identification_tag" Name="swid:entitlement_required_indicator" Condition="1" Order="0" Flags="14" Text="false"/> <ROW XmlElement="swidentitlement_required_indicator" ParentElement="swidsoftware_identification_tag" Name="swid:entitlement_required_indicator" Condition="1" Order="0" Flags="14" Text="false"/>
<ROW XmlElement="swidmajor" ParentElement="swidnumeric" Name="swid:major" Condition="1" Order="0" Flags="14" Text="1"/> <ROW XmlElement="swidmajor" ParentElement="swidnumeric" Name="swid:major" Condition="1" Order="0" Flags="14" Text="1"/>
<ROW XmlElement="swidminor" ParentElement="swidnumeric" Name="swid:minor" Condition="1" Order="1" Flags="14" Text="1"/> <ROW XmlElement="swidminor" ParentElement="swidnumeric" Name="swid:minor" Condition="1" Order="1" Flags="14" Text="1"/>

View File

@@ -53,7 +53,6 @@ POSSIBILITY OF SUCH DAMAGE.
#undef USE_PROC_NET_ROUTE #undef USE_PROC_NET_ROUTE
#define USE_SOCKET_ROUTE #define USE_SOCKET_ROUTE
#undef USE_SYSCTL_NET_ROUTE #undef USE_SYSCTL_NET_ROUTE
#include <sys/sysctl.h>
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
@@ -97,6 +96,7 @@ POSSIBILITY OF SUCH DAMAGE.
#ifdef USE_SYSCTL_NET_ROUTE #ifdef USE_SYSCTL_NET_ROUTE
#include <stdlib.h> #include <stdlib.h>
#include <sys/sysctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <net/route.h> #include <net/route.h>
#endif #endif

View File

@@ -1,16 +1,6 @@
$Id: Changelog.txt,v 1.223 2016/04/19 21:06:20 nanard Exp $ $Id: Changelog.txt,v 1.219 2015/10/26 17:05:06 nanard Exp $
miniUPnP client Changelog. miniUPnP client Changelog.
VERSION 2.0 : released 2016/04/19
2016/01/24:
change miniwget to return HTTP status code
increments API_VERSION to 16
2016/01/22:
Improve UPNPIGD_IsConnected() to check if WAN address is not private.
parse HTTP response status line in miniwget.c
2015/10/26: 2015/10/26:
snprintf() overflow check. check overflow in simpleUPnPcommand2() snprintf() overflow check. check overflow in simpleUPnPcommand2()

View File

@@ -3,7 +3,7 @@ Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
github: https://github.com/miniupnp/miniupnp github: https://github.com/miniupnp/miniupnp
freecode: http://freecode.com/projects/miniupnp freecode: http://freecode.com/projects/miniupnp
Author: Thomas Bernard Author: Thomas Bernard
Copyright (c) 2005-2016 Thomas Bernard Copyright (c) 2005-2014 Thomas Bernard
This software is subject to the conditions detailed in the This software is subject to the conditions detailed in the
LICENSE file provided within this distribution. LICENSE file provided within this distribution.
@@ -32,7 +32,6 @@ To use the libminiupnpc in your application, link it with
libminiupnpc.a (or .so) and use the following functions found in miniupnpc.h, libminiupnpc.a (or .so) and use the following functions found in miniupnpc.h,
upnpcommands.h and miniwget.h : upnpcommands.h and miniwget.h :
- upnpDiscover() - upnpDiscover()
- UPNP_GetValidIGD()
- miniwget() - miniwget()
- parserootdesc() - parserootdesc()
- GetUPNPUrls() - GetUPNPUrls()
@@ -60,5 +59,3 @@ send me an email !
For any question, you can use the web forum : For any question, you can use the web forum :
http://miniupnp.tuxfamily.org/forum/ http://miniupnp.tuxfamily.org/forum/
Bugs should be reported on github :
https://github.com/miniupnp/miniupnp/issues

View File

@@ -1 +1 @@
2.0 1.9

View File

@@ -1,12 +1,7 @@
$Id: apiversions.txt,v 1.9 2016/01/24 17:24:36 nanard Exp $ $Id: apiversions.txt,v 1.8 2015/10/08 16:15:47 nanard Exp $
Differences in API between miniUPnPc versions Differences in API between miniUPnPc versions
API version 16
added "status_code" argument to getHTTPResponse(), miniwget() and miniwget_getaddr()
updated macro :
#define MINIUPNPC_API_VERSION 16
API version 15 API version 15
changed "sameport" argument of upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice() changed "sameport" argument of upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
to "localport". When 0 or 1, behaviour is not changed, but it can take to "localport". When 0 or 1, behaviour is not changed, but it can take

View File

@@ -1,4 +1,4 @@
/* $Id: minihttptestserver.c,v 1.19 2015/11/17 09:07:17 nanard Exp $ */ /* $Id: minihttptestserver.c,v 1.18 2015/07/15 12:41:15 nanard Exp $ */
/* Project : miniUPnP /* Project : miniUPnP
* Author : Thomas Bernard * Author : Thomas Bernard
* Copyright (c) 2011-2015 Thomas Bernard * Copyright (c) 2011-2015 Thomas Bernard
@@ -18,10 +18,6 @@
#include <time.h> #include <time.h>
#include <errno.h> #include <errno.h>
#ifndef INADDR_LOOPBACK
#define INADDR_LOOPBACK 0x7f000001
#endif
#define CRAP_LENGTH (2048) #define CRAP_LENGTH (2048)
volatile sig_atomic_t quit = 0; volatile sig_atomic_t quit = 0;

View File

@@ -22,7 +22,7 @@
#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 "1.9"
#define UPNP_VERSION_STRING "UPnP/1.1" #define UPNP_VERSION_STRING "UPnP/1.1"
#endif #endif

View File

@@ -1,8 +1,7 @@
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS
/* $Id: minissdpc.c,v 1.31 2016/01/19 09:56:46 nanard Exp $ */ /* $Id: minissdpc.c,v 1.30 2015/10/26 17:05:07 nanard Exp $ */
/* 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-2015 Thomas Bernard
@@ -70,10 +69,6 @@ struct sockaddr_un {
#define HAS_IP_MREQN #define HAS_IP_MREQN
#endif #endif
#if !defined(HAS_IP_MREQN) && !defined(_WIN32)
#include <sys/ioctl.h>
#endif
#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN) #if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
/* Several versions of glibc don't define this structure, /* Several versions of glibc don't define this structure,
* define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */ * define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */
@@ -654,25 +649,11 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
{ {
PRINT_SOCKET_ERROR("setsockopt"); PRINT_SOCKET_ERROR("setsockopt");
} }
#elif !defined(_WIN32) #else
struct ifreq ifr;
int ifrlen = sizeof(ifr);
strncpy(ifr.ifr_name, multicastif, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ-1] = '\0';
if(ioctl(sudp, SIOCGIFADDR, &ifr, &ifrlen) < 0)
{
PRINT_SOCKET_ERROR("ioctl(...SIOCGIFADDR...)");
}
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)
{
PRINT_SOCKET_ERROR("setsockopt");
}
#else /* _WIN32 */
#ifdef DEBUG #ifdef DEBUG
printf("Setting of multicast interface not supported with interface name.\n"); printf("Setting of multicast interface not supported with interface name.\n");
#endif #endif
#endif /* #ifdef HAS_IP_MREQN / !defined(_WIN32) */ #endif
} }
} }
} }

View File

@@ -1,11 +1,10 @@
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS
/* $Id: miniupnpc.c,v 1.141 2015/10/26 17:05:07 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
* Web : http://miniupnp.free.fr/ * Web : http://miniupnp.free.fr/
* Author : Thomas BERNARD * Author : Thomas BERNARD
* copyright (c) 2005-2016 Thomas Bernard * copyright (c) 2005-2015 Thomas Bernard
* This software is subjet to the conditions detailed in the * This software is subjet to the conditions detailed in the
* provided LICENSE file. */ * provided LICENSE file. */
#include <stdlib.h> #include <stdlib.h>
@@ -74,25 +73,6 @@
#define SERVICEPREFIX "u" #define SERVICEPREFIX "u"
#define SERVICEPREFIX2 'u' #define SERVICEPREFIX2 'u'
/* check if an ip address is a private (LAN) address
* see https://tools.ietf.org/html/rfc1918 */
static int is_rfc1918addr(const char * addr)
{
/* 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) */
if(COMPARE(addr, "192.168."))
return 1;
/* 10.0.0.0 - 10.255.255.255 (10/8 prefix) */
if(COMPARE(addr, "10."))
return 1;
/* 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) */
if(COMPARE(addr, "172.")) {
int i = atoi(addr + 4);
if((16 <= i) && (i <= 31))
return 1;
}
return 0;
}
/* root description parsing */ /* root description parsing */
MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data) MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
{ {
@@ -128,7 +108,6 @@ char * simpleUPnPcommand2(int s, const char * url, const char * service,
int soapbodylen; int soapbodylen;
char * buf; char * buf;
int n; int n;
int status_code;
*bufsize = 0; *bufsize = 0;
snprintf(soapact, sizeof(soapact), "%s#%s", service, action); snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
@@ -232,15 +211,11 @@ char * simpleUPnPcommand2(int s, const char * url, const char * service,
return NULL; return NULL;
} }
buf = getHTTPResponse(s, bufsize, &status_code); buf = getHTTPResponse(s, bufsize);
#ifdef DEBUG #ifdef DEBUG
if(*bufsize > 0 && buf) if(*bufsize > 0 && buf)
{ {
printf("HTTP %d SOAP Response :\n%.*s\n", status_code, *bufsize, buf); printf("SOAP Response :\n%.*s\n", *bufsize, buf);
}
else
{
printf("HTTP %d, empty SOAP response. size=%d\n", status_code, *bufsize);
} }
#endif #endif
closesocket(s); closesocket(s);
@@ -552,7 +527,7 @@ UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
* 3 = an UPnP device has been found but was not recognized as an IGD * 3 = an UPnP device has been found but was not recognized as an IGD
* *
* In any positive non zero return case, the urls and data structures * In any positive non zero return case, the urls and data structures
* passed as parameters are set. Dont forget to call FreeUPNPUrls(urls) to * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
* free allocated memory. * free allocated memory.
*/ */
MINIUPNP_LIBSPEC int MINIUPNP_LIBSPEC int
@@ -572,9 +547,6 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */ int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
int n_igd = 0; int n_igd = 0;
char extIpAddr[16]; char extIpAddr[16];
char myLanAddr[40];
int status_code = -1;
if(!devlist) if(!devlist)
{ {
#ifdef DEBUG #ifdef DEBUG
@@ -597,8 +569,8 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
/* we should choose an internet gateway device. /* we should choose an internet gateway device.
* with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */ * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size), desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
myLanAddr, sizeof(myLanAddr), lanaddr, lanaddrlen,
dev->scope_id, &status_code); dev->scope_id);
#ifdef DEBUG #ifdef DEBUG
if(!desc[i].xml) if(!desc[i].xml)
{ {
@@ -615,8 +587,6 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
{ {
desc[i].is_igd = 1; desc[i].is_igd = 1;
n_igd++; n_igd++;
if(lanaddr)
strncpy(lanaddr, myLanAddr, lanaddrlen);
} }
} }
} }
@@ -632,25 +602,20 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
parserootdesc(desc[i].xml, desc[i].size, data); parserootdesc(desc[i].xml, desc[i].size, data);
if(desc[i].is_igd || state >= 3 ) if(desc[i].is_igd || state >= 3 )
{ {
int is_connected;
GetUPNPUrls(urls, data, dev->descURL, dev->scope_id); GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
/* in state 2 and 3 we dont test if device is connected ! */ /* in state 2 and 3 we dont test if device is connected ! */
if(state >= 2) if(state >= 2)
goto free_and_return; goto free_and_return;
is_connected = UPNPIGD_IsConnected(urls, data);
#ifdef DEBUG #ifdef DEBUG
printf("UPNPIGD_IsConnected(%s) = %d\n", printf("UPNPIGD_IsConnected(%s) = %d\n",
urls->controlURL, is_connected); urls->controlURL,
UPNPIGD_IsConnected(urls, data));
#endif #endif
/* checks that status is connected AND there is a external IP address assigned */ /* checks that status is connected AND there is a external IP address assigned */
if(is_connected && if(UPNPIGD_IsConnected(urls, data)
(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) { && (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0))
if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
&& (0 != strcmp(extIpAddr, "0.0.0.0")))
goto free_and_return; goto free_and_return;
}
FreeUPNPUrls(urls); FreeUPNPUrls(urls);
if(data->second.servicetype[0] != '\0') { if(data->second.servicetype[0] != '\0') {
#ifdef DEBUG #ifdef DEBUG
@@ -662,17 +627,14 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service)); memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service)); memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
GetUPNPUrls(urls, data, dev->descURL, dev->scope_id); GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
is_connected = UPNPIGD_IsConnected(urls, data);
#ifdef DEBUG #ifdef DEBUG
printf("UPNPIGD_IsConnected(%s) = %d\n", printf("UPNPIGD_IsConnected(%s) = %d\n",
urls->controlURL, is_connected); urls->controlURL,
UPNPIGD_IsConnected(urls, data));
#endif #endif
if(is_connected && if(UPNPIGD_IsConnected(urls, data)
(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) { && (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0))
if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
&& (0 != strcmp(extIpAddr, "0.0.0.0")))
goto free_and_return; goto free_and_return;
}
FreeUPNPUrls(urls); FreeUPNPUrls(urls);
} }
} }
@@ -706,9 +668,8 @@ UPNP_GetIGDFromUrl(const char * rootdescurl,
{ {
char * descXML; char * descXML;
int descXMLsize = 0; int descXMLsize = 0;
descXML = miniwget_getaddr(rootdescurl, &descXMLsize, descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
lanaddr, lanaddrlen, 0, NULL); lanaddr, lanaddrlen, 0);
if(descXML) { if(descXML) {
memset(data, 0, sizeof(struct IGDdatas)); memset(data, 0, sizeof(struct IGDdatas));
memset(urls, 0, sizeof(struct UPNPUrls)); memset(urls, 0, sizeof(struct UPNPUrls));

View File

@@ -1,8 +1,8 @@
/* $Id: miniupnpc.h,v 1.50 2016/04/19 21:06:21 nanard Exp $ */ /* $Id: miniupnpc.h,v 1.48 2015/10/08 16:19:40 nanard Exp $ */
/* Project: miniupnp /* Project: miniupnp
* http://miniupnp.free.fr/ * http://miniupnp.free.fr/
* Author: Thomas Bernard * Author: Thomas Bernard
* Copyright (c) 2005-2016 Thomas Bernard * Copyright (c) 2005-2015 Thomas Bernard
* This software is subjects to the conditions detailed * This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */ * in the LICENCE file provided within this distribution */
#ifndef MINIUPNPC_H_INCLUDED #ifndef MINIUPNPC_H_INCLUDED
@@ -19,8 +19,8 @@
#define UPNPDISCOVER_MEMORY_ERROR (-102) #define UPNPDISCOVER_MEMORY_ERROR (-102)
/* versions : */ /* versions : */
#define MINIUPNPC_VERSION "2.0" #define MINIUPNPC_VERSION "1.9.20151026"
#define MINIUPNPC_API_VERSION 16 #define MINIUPNPC_API_VERSION 15
/* Source port: /* Source port:
Using "1" as an alias for 1900 for backwards compatability Using "1" as an alias for 1900 for backwards compatability

View File

@@ -1,10 +1,9 @@
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS
/* $Id: miniwget.c,v 1.72 2015/10/26 17:05:08 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
* Copyright (c) 2005-2016 Thomas Bernard * Copyright (c) 2005-2015 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. */
@@ -50,12 +49,12 @@
#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 "1.9"
#define UPNP_VERSION_STRING "UPnP/1.1" #define UPNP_VERSION_STRING "UPnP/1.1"
#endif #endif
#include "miniwget.h" #include "miniwget.h"
#include "connecthostport.h" #include "connecthostport.h"
#include "receivedata.h" #include "receivedata.h"
@@ -71,7 +70,7 @@
* to the length parameter. * to the length parameter.
*/ */
void * void *
getHTTPResponse(int s, int * size, int * status_code) getHTTPResponse(int s, int * size)
{ {
char buf[2048]; char buf[2048];
int n; int n;
@@ -89,10 +88,7 @@ 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;
char * reason_phrase = NULL;
int reason_phrase_len = 0;
if(status_code) *status_code = -1;
header_buf = malloc(header_buf_len); header_buf = malloc(header_buf_len);
if(header_buf == NULL) if(header_buf == NULL)
{ {
@@ -164,7 +160,7 @@ getHTTPResponse(int s, int * size, int * status_code)
continue; continue;
/* parse header lines */ /* parse header lines */
for(i = 0; i < endofheaders - 1; i++) { for(i = 0; i < endofheaders - 1; i++) {
if(linestart > 0 && colon <= linestart && header_buf[i]==':') if(colon <= linestart && header_buf[i]==':')
{ {
colon = i; colon = i;
while(i < (endofheaders-1) while(i < (endofheaders-1)
@@ -175,29 +171,7 @@ getHTTPResponse(int s, int * size, int * status_code)
/* detecting end of line */ /* detecting end of line */
else if(header_buf[i]=='\r' || header_buf[i]=='\n') else if(header_buf[i]=='\r' || header_buf[i]=='\n')
{ {
if(linestart == 0 && status_code) if(colon > linestart && valuestart > colon)
{
/* Status line
* HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
int sp;
for(sp = 0; sp < i; sp++)
if(header_buf[sp] == ' ')
{
if(*status_code < 0)
*status_code = atoi(header_buf + sp + 1);
else
{
reason_phrase = header_buf + sp + 1;
reason_phrase_len = i - sp - 1;
break;
}
}
#ifdef DEBUG
printf("HTTP status code = %d, Reason phrase = %.*s\n",
*status_code, reason_phrase_len, reason_phrase);
#endif
}
else if(colon > linestart && valuestart > colon)
{ {
#ifdef DEBUG #ifdef DEBUG
printf("header='%.*s', value='%.*s'\n", printf("header='%.*s', value='%.*s'\n",
@@ -368,8 +342,7 @@ static void *
miniwget3(const char * host, miniwget3(const char * host,
unsigned short port, const char * path, unsigned short port, const char * path,
int * size, char * addr_str, int addr_str_len, int * size, char * addr_str, int addr_str_len,
const char * httpversion, unsigned int scope_id, const char * httpversion, unsigned int scope_id)
int * status_code)
{ {
char buf[2048]; char buf[2048];
int s; int s;
@@ -467,7 +440,7 @@ miniwget3(const char * host,
sent += n; sent += n;
} }
} }
content = getHTTPResponse(s, size, status_code); content = getHTTPResponse(s, size);
closesocket(s); closesocket(s);
return content; return content;
} }
@@ -478,18 +451,16 @@ static void *
miniwget2(const char * host, miniwget2(const char * host,
unsigned short port, const char * path, unsigned short port, const char * path,
int * size, char * addr_str, int addr_str_len, int * size, char * addr_str, int addr_str_len,
unsigned int scope_id, int * status_code) unsigned int scope_id)
{ {
char * respbuffer; char * respbuffer;
#if 1 #if 1
respbuffer = miniwget3(host, port, path, size, respbuffer = miniwget3(host, port, path, size,
addr_str, addr_str_len, "1.1", addr_str, addr_str_len, "1.1", scope_id);
scope_id, status_code);
#else #else
respbuffer = miniwget3(host, port, path, size, respbuffer = miniwget3(host, port, path, size,
addr_str, addr_str_len, "1.0", addr_str, addr_str_len, "1.0", scope_id);
scope_id, status_code);
if (*size == 0) if (*size == 0)
{ {
#ifdef DEBUG #ifdef DEBUG
@@ -497,8 +468,7 @@ miniwget2(const char * host,
#endif #endif
free(respbuffer); free(respbuffer);
respbuffer = miniwget3(host, port, path, size, respbuffer = miniwget3(host, port, path, size,
addr_str, addr_str_len, "1.1", addr_str, addr_str_len, "1.1", scope_id);
scope_id, status_code);
} }
#endif #endif
return respbuffer; return respbuffer;
@@ -623,8 +593,7 @@ parseURL(const char * url,
} }
void * void *
miniwget(const char * url, int * size, miniwget(const char * url, int * size, unsigned int scope_id)
unsigned int scope_id, int * status_code)
{ {
unsigned short port; unsigned short port;
char * path; char * path;
@@ -637,13 +606,12 @@ miniwget(const char * url, int * size,
printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n", printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
hostname, port, path, scope_id); hostname, port, path, scope_id);
#endif #endif
return miniwget2(hostname, port, path, size, 0, 0, scope_id, status_code); return miniwget2(hostname, port, path, size, 0, 0, scope_id);
} }
void * void *
miniwget_getaddr(const char * url, int * size, miniwget_getaddr(const char * url, int * size,
char * addr, int addrlen, unsigned int scope_id, char * addr, int addrlen, unsigned int scope_id)
int * status_code)
{ {
unsigned short port; unsigned short port;
char * path; char * path;
@@ -658,6 +626,5 @@ miniwget_getaddr(const char * url, int * size,
printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n", printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
hostname, port, path, scope_id); hostname, port, path, scope_id);
#endif #endif
return miniwget2(hostname, port, path, size, addr, addrlen, scope_id, status_code); return miniwget2(hostname, port, path, size, addr, addrlen, scope_id);
} }

View File

@@ -1,7 +1,7 @@
/* $Id: miniwget.h,v 1.12 2016/01/24 17:24:36 nanard Exp $ */ /* $Id: miniwget.h,v 1.10 2015/07/21 13:16:55 nanard Exp $ */
/* Project : miniupnp /* Project : miniupnp
* Author : Thomas Bernard * Author : Thomas Bernard
* Copyright (c) 2005-2016 Thomas Bernard * Copyright (c) 2005-2015 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.
* */ * */
@@ -14,11 +14,11 @@
extern "C" { extern "C" {
#endif #endif
MINIUPNP_LIBSPEC void * getHTTPResponse(int s, int * size, int * status_code); MINIUPNP_LIBSPEC void * getHTTPResponse(int s, int * size);
MINIUPNP_LIBSPEC void * miniwget(const char *, int *, unsigned int, int *); MINIUPNP_LIBSPEC void * miniwget(const char *, int *, unsigned int);
MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int, int *); MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int);
int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *); int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *);

View File

@@ -1,4 +1,4 @@
/* $Id: receivedata.c,v 1.7 2015/11/09 21:51:41 nanard Exp $ */ /* $Id: receivedata.c,v 1.6 2014/11/13 13:51:52 nanard Exp $ */
/* Project : miniupnp /* Project : miniupnp
* Website : http://miniupnp.free.fr/ * Website : http://miniupnp.free.fr/
* Author : Thomas Bernard * Author : Thomas Bernard
@@ -40,7 +40,7 @@ receivedata(int socket,
char * data, int length, char * data, int length,
int timeout, unsigned int * scope_id) int timeout, unsigned int * scope_id)
{ {
#ifdef MINIUPNPC_GET_SRC_ADDR #if MINIUPNPC_GET_SRC_ADDR
struct sockaddr_storage src_addr; struct sockaddr_storage src_addr;
socklen_t src_addr_len = sizeof(src_addr); socklen_t src_addr_len = sizeof(src_addr);
#endif /* MINIUPNPC_GET_SRC_ADDR */ #endif /* MINIUPNPC_GET_SRC_ADDR */
@@ -80,7 +80,7 @@ receivedata(int socket,
return 0; return 0;
} }
#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ #endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
#ifdef MINIUPNPC_GET_SRC_ADDR #if MINIUPNPC_GET_SRC_ADDR
memset(&src_addr, 0, sizeof(src_addr)); memset(&src_addr, 0, sizeof(src_addr));
n = recvfrom(socket, data, length, 0, n = recvfrom(socket, data, length, 0,
(struct sockaddr *)&src_addr, &src_addr_len); (struct sockaddr *)&src_addr, &src_addr_len);
@@ -90,7 +90,7 @@ receivedata(int socket,
if(n<0) { if(n<0) {
PRINT_SOCKET_ERROR("recv"); PRINT_SOCKET_ERROR("recv");
} }
#ifdef MINIUPNPC_GET_SRC_ADDR #if MINIUPNPC_GET_SRC_ADDR
if (src_addr.ss_family == AF_INET6) { if (src_addr.ss_family == AF_INET6) {
const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr; const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr;
#ifdef DEBUG #ifdef DEBUG

View File

@@ -1,7 +1,7 @@
/* $Id: testminiwget.c,v 1.5 2016/01/24 17:24:36 nanard Exp $ */ /* $Id: testminiwget.c,v 1.4 2012/06/23 22:35:59 nanard Exp $ */
/* Project : miniupnp /* Project : miniupnp
* Author : Thomas Bernard * Author : Thomas Bernard
* Copyright (c) 2005-2016 Thomas Bernard * Copyright (c) 2005-2012 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.
* */ * */
@@ -20,17 +20,15 @@ int main(int argc, char * * argv)
int size, writtensize; int size, writtensize;
FILE *f; FILE *f;
char addr[64]; char addr[64];
int status_code = -1;
if(argc < 3) { if(argc < 3) {
fprintf(stderr, "Usage:\t%s url file\n", argv[0]); fprintf(stderr, "Usage:\t%s url file\n", argv[0]);
fprintf(stderr, "Example:\t%s http://www.google.com/ out.html\n", argv[0]); fprintf(stderr, "Example:\t%s http://www.google.com/ out.html\n", argv[0]);
return 1; return 1;
} }
data = miniwget_getaddr(argv[1], &size, addr, sizeof(addr), 0, &status_code); data = miniwget_getaddr(argv[1], &size, addr, sizeof(addr), 0);
if(!data || (status_code != 200)) { if(!data) {
if(data) free(data); fprintf(stderr, "Error fetching %s\n", argv[1]);
fprintf(stderr, "Error %d fetching %s\n", status_code, argv[1]);
return 1; return 1;
} }
printf("local address : %s\n", addr); printf("local address : %s\n", addr);

View File

@@ -1,7 +1,7 @@
/* $Id: upnpc.c,v 1.114 2016/01/22 15:04:23 nanard Exp $ */ /* $Id: upnpc.c,v 1.112 2015/10/08 16:15:48 nanard Exp $ */
/* Project : miniupnp /* Project : miniupnp
* Author : Thomas Bernard * Author : Thomas Bernard
* Copyright (c) 2005-2016 Thomas Bernard * Copyright (c) 2005-2015 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. */
@@ -66,7 +66,7 @@ static void DisplayInfos(struct UPNPUrls * urls,
char connectionType[64]; char connectionType[64];
char status[64]; char status[64];
char lastconnerr[64]; char lastconnerr[64];
unsigned int uptime = 0; unsigned int uptime;
unsigned int brUp, brDown; unsigned int brUp, brDown;
time_t timenow, timestarted; time_t timenow, timestarted;
int r; int r;
@@ -82,11 +82,9 @@ static void DisplayInfos(struct UPNPUrls * urls,
else else
printf("Status : %s, uptime=%us, LastConnectionError : %s\n", printf("Status : %s, uptime=%us, LastConnectionError : %s\n",
status, uptime, lastconnerr); status, uptime, lastconnerr);
if(uptime > 0) {
timenow = time(NULL); timenow = time(NULL);
timestarted = timenow - uptime; timestarted = timenow - uptime;
printf(" Time started : %s", ctime(&timestarted)); printf(" Time started : %s", ctime(&timestarted));
}
if(UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->CIF.servicetype, if(UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->CIF.servicetype,
&brDown, &brUp) != UPNPCOMMAND_SUCCESS) { &brDown, &brUp) != UPNPCOMMAND_SUCCESS) {
printf("GetLinkLayerMaxBitRates failed.\n"); printf("GetLinkLayerMaxBitRates failed.\n");
@@ -540,7 +538,7 @@ int main(int argc, char ** argv)
char ** commandargv = 0; char ** commandargv = 0;
int commandargc = 0; int commandargc = 0;
struct UPNPDev * devlist = 0; struct UPNPDev * devlist = 0;
char lanaddr[64] = "unset"; /* my ip address on the LAN */ char lanaddr[64]; /* my ip address on the LAN */
int i; int i;
const char * rootdescurl = 0; const char * rootdescurl = 0;
const char * multicastif = 0; const char * multicastif = 0;
@@ -562,7 +560,7 @@ int main(int argc, char ** argv)
} }
#endif #endif
printf("upnpc : miniupnpc library test client, version %s.\n", MINIUPNPC_VERSION_STRING); printf("upnpc : miniupnpc library test client, version %s.\n", MINIUPNPC_VERSION_STRING);
printf(" (c) 2005-2016 Thomas Bernard.\n"); printf(" (c) 2005-2015 Thomas Bernard.\n");
printf("Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/\n" printf("Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/\n"
"for more information.\n"); "for more information.\n");
/* command line processing */ /* command line processing */

View File

@@ -1,6 +1,5 @@
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS
/* $Id: upnpcommands.c,v 1.46 2015/07/15 12:19:00 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
* Copyright (c) 2005-2015 Thomas Bernard * Copyright (c) 2005-2015 Thomas Bernard
@@ -618,14 +617,14 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL,
protocol[3] = '\0'; protocol[3] = '\0';
} }
p = GetValueFromNameValueList(&pdata, "NewInternalClient"); p = GetValueFromNameValueList(&pdata, "NewInternalClient");
if(p) if(p && intClient)
{ {
strncpy(intClient, p, 16); strncpy(intClient, p, 16);
intClient[15] = '\0'; intClient[15] = '\0';
r = 0; r = 0;
} }
p = GetValueFromNameValueList(&pdata, "NewInternalPort"); p = GetValueFromNameValueList(&pdata, "NewInternalPort");
if(p) if(p && intPort)
{ {
strncpy(intPort, p, 6); strncpy(intPort, p, 6);
intPort[5] = '\0'; intPort[5] = '\0';

View File

@@ -96,51 +96,26 @@ 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
*/ */
#define ZT_MAX_PEER_NETWORK_PATHS 4 #define ZT_MAX_PEER_NETWORK_PATHS 4
/**
* Maximum number of trusted physical network paths
*/
#define ZT_MAX_TRUSTED_PATHS 16
/**
* Maximum number of rules per capability
*/
#define ZT_MAX_CAPABILITY_RULES 64
/**
* 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
* *
@@ -154,11 +129,6 @@ 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)
*/ */
@@ -174,81 +144,6 @@ extern "C" {
*/ */
#define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1500 - 48) #define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1500 - 48)
/**
* 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: 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
*/ */
@@ -467,7 +362,7 @@ enum ZT_VirtualNetworkStatus
ZT_NETWORK_STATUS_PORT_ERROR = 4, ZT_NETWORK_STATUS_PORT_ERROR = 4,
/** /**
* ZeroTier core version too old * ZeroTier One version too old
*/ */
ZT_NETWORK_STATUS_CLIENT_TOO_OLD = 5 ZT_NETWORK_STATUS_CLIENT_TOO_OLD = 5
}; };
@@ -491,16 +386,12 @@ 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) because the most significant bit * These must range from 0 to 127 (0x7f).
* is reserved as a NOT flag.
* *
* Each rule is composed of zero or more MATCHes followed by an ACTION. * Each rule is composed of one or more MATCHes followed by an ACTION.
* An ACTION with no MATCHes is always taken.
*/ */
enum ZT_VirtualNetworkRuleType enum ZT_VirtualNetworkRuleType
{ {
// 0 to 31 reserved for actions
/** /**
* Drop frame * Drop frame
*/ */
@@ -512,31 +403,14 @@ enum ZT_VirtualNetworkRuleType
ZT_NETWORK_RULE_ACTION_ACCEPT = 1, ZT_NETWORK_RULE_ACTION_ACCEPT = 1,
/** /**
* Forward a copy of this frame to an observer (by ZT address) * Forward a copy of this frame to an observer
*/ */
ZT_NETWORK_RULE_ACTION_TEE = 2, ZT_NETWORK_RULE_ACTION_TEE = 2,
/** /**
* Exactly like TEE but mandates ACKs from observer * Explicitly redirect this frame to another device (ignored if this is the target device)
*/ */
ZT_NETWORK_RULE_ACTION_WATCH = 3, ZT_NETWORK_RULE_ACTION_REDIRECT = 3,
/**
* Drop and redirect this frame to another node (by ZT address)
*/
ZT_NETWORK_RULE_ACTION_REDIRECT = 4,
/**
* Log if match and if rule debugging is enabled in the build, otherwise does nothing (for developers)
*/
ZT_NETWORK_RULE_ACTION_DEBUG_LOG = 5,
/**
* Maximum ID for an ACTION, anything higher is a MATCH
*/
ZT_NETWORK_RULE_ACTION__MAX_ID = 31,
// 32 to 127 reserved for match criteria
/** /**
* Source ZeroTier address -- analogous to an Ethernet port ID on a switch * Source ZeroTier address -- analogous to an Ethernet port ID on a switch
@@ -608,60 +482,38 @@ enum ZT_VirtualNetworkRuleType
*/ */
ZT_NETWORK_RULE_MATCH_IP_PROTOCOL = 45, ZT_NETWORK_RULE_MATCH_IP_PROTOCOL = 45,
/**
* ICMP type and possibly code (does not match if not ICMP)
*/
ZT_NETWORK_RULE_MATCH_ICMP = 46,
/** /**
* IP source port range (start-end, inclusive) * IP source port range (start-end, inclusive)
*/ */
ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE = 47, ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE = 46,
/** /**
* IP destination port range (start-end, inclusive) * IP destination port range (start-end, inclusive)
*/ */
ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE = 48, ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE = 47,
/** /**
* Packet characteristics (set of flags) * Packet characteristics (set of flags)
*/ */
ZT_NETWORK_RULE_MATCH_CHARACTERISTICS = 49, ZT_NETWORK_RULE_MATCH_CHARACTERISTICS = 48,
/** /**
* Frame size range (start-end, inclusive) * Frame size range (start-end, inclusive)
*/ */
ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE = 50, ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE = 49,
/** /**
* Match if local and remote tags differ by no more than value, use 0 to check for equality * Match a range of relative TCP sequence numbers (e.g. approx first N bytes of stream)
*/ */
ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE = 51, ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE = 50
/**
* Match if local and remote tags ANDed together equal value.
*/
ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND = 52,
/**
* Match if local and remote tags ANDed together equal value.
*/
ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR = 53,
/**
* Match if local and remote tags XORed together equal value.
*/
ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR = 54,
/**
* Maximum ID allowed for a MATCH entry in the rules table
*/
ZT_NETWORK_RULE_MATCH__MAX_ID = 127
}; };
/** /**
* 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
@@ -708,13 +560,18 @@ typedef struct
/** /**
* Packet characteristic flags being matched * Packet characteristic flags being matched
*/ */
uint64_t characteristics[2]; uint64_t characteristics;
/** /**
* IP port range -- start-end inclusive -- host byte order * IP port range -- start-end inclusive -- host byte order
*/ */
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)
*/ */
@@ -751,7 +608,7 @@ typedef struct
uint8_t ipProtocol; uint8_t ipProtocol;
/** /**
* IP type of service a.k.a. DSCP field * IP type of service
*/ */
uint8_t ipTos; uint8_t ipTos;
@@ -759,54 +616,9 @@ typedef struct
* Ethernet packet size in host byte order (start-end, inclusive) * Ethernet packet size in host byte order (start-end, inclusive)
*/ */
uint16_t frameSize[2]; uint16_t frameSize[2];
/**
* ICMP type and code
*/
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
*/ */
@@ -821,16 +633,6 @@ typedef struct
* Gateway IP address (port ignored) or NULL (family == 0) for LAN-local (no gateway) * Gateway IP address (port ignored) or NULL (family == 0) for LAN-local (no gateway)
*/ */
struct sockaddr_storage via; struct sockaddr_storage via;
/**
* Route flags
*/
uint16_t flags;
/**
* Route metric (not currently used)
*/
uint16_t metric;
} ZT_VirtualNetworkRoute; } ZT_VirtualNetworkRoute;
/** /**
@@ -875,28 +677,19 @@ enum ZT_VirtualNetworkConfigOperation
ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY = 4 ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY = 4
}; };
enum ZT_RelayPolicy
{
ZT_RELAY_POLICY_NEVER = 0,
ZT_RELAY_POLICY_TRUSTED = 1,
ZT_RELAY_POLICY_ALWAYS = 2
};
/** /**
* 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_UPSTREAM = 1, // upstream node ZT_PEER_ROLE_RELAY = 1, // relay node
ZT_PEER_ROLE_ROOT = 2 // global root ZT_PEER_ROLE_ROOT = 2 // root server
}; };
/** /**
* 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
}; };
@@ -904,8 +697,7 @@ 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,
@@ -920,15 +712,13 @@ 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,
@@ -937,14 +727,7 @@ enum ZT_Architecture
ZT_ARCHITECTURE_MIPS32 = 5, ZT_ARCHITECTURE_MIPS32 = 5,
ZT_ARCHITECTURE_MIPS64 = 6, ZT_ARCHITECTURE_MIPS64 = 6,
ZT_ARCHITECTURE_POWER32 = 7, ZT_ARCHITECTURE_POWER32 = 7,
ZT_ARCHITECTURE_POWER64 = 8, ZT_ARCHITECTURE_POWER64 = 8
ZT_ARCHITECTURE_OPENRISC32 = 9,
ZT_ARCHITECTURE_OPENRISC64 = 10,
ZT_ARCHITECTURE_SPARC32 = 11,
ZT_ARCHITECTURE_SPARC64 = 12,
ZT_ARCHITECTURE_DOTNET_CLR = 13,
ZT_ARCHITECTURE_JAVA_JVM = 14,
ZT_ARCHITECTURE_WEB = 15
}; };
/** /**
@@ -982,11 +765,6 @@ 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
* *
@@ -1010,15 +788,32 @@ typedef struct
int broadcastEnabled; int broadcastEnabled;
/** /**
* If the network is in PORT_ERROR state, this is the (negative) error code most recently reported * If the network is in PORT_ERROR state, this is the error most recently returned by the port config callback
*/ */
int portError; int portError;
/** /**
* Revision number as reported by controller or 0 if still waiting for config * Is this network enabled? If not, all frames to/from are dropped.
*/
int enabled;
/**
* Network config revision as reported by netconf master
*
* If this is zero, it means we're still waiting for our netconf.
*/ */
unsigned long netconfRevision; unsigned long netconfRevision;
/**
* Number of multicast group subscriptions
*/
unsigned int multicastSubscriptionCount;
/**
* Multicast group subscriptions
*/
ZT_MulticastGroup multicastSubscriptions[ZT_MAX_NETWORK_MULTICAST_SUBSCRIPTIONS];
/** /**
* Number of assigned addresses * Number of assigned addresses
*/ */
@@ -1035,16 +830,6 @@ typedef struct
* virtual network's configuration master. * virtual network's configuration master.
*/ */
struct sockaddr_storage assignedAddresses[ZT_MAX_ZT_ASSIGNED_ADDRESSES]; struct sockaddr_storage assignedAddresses[ZT_MAX_ZT_ASSIGNED_ADDRESSES];
/**
* Number of ZT-pushed routes
*/
unsigned int routeCount;
/**
* Routes (excluding those implied by assigned addresses and their masks)
*/
ZT_VirtualNetworkRoute routes[ZT_MAX_NETWORK_ROUTES];
} ZT_VirtualNetworkConfig; } ZT_VirtualNetworkConfig;
/** /**
@@ -1077,14 +862,9 @@ typedef struct
uint64_t lastReceive; uint64_t lastReceive;
/** /**
* Is this a trusted path? If so this will be its nonzero ID. * Is path active?
*/ */
uint64_t trustedPathId; int active;
/**
* Is path expired?
*/
int expired;
/** /**
* Is path preferred? * Is path preferred?
@@ -1246,13 +1026,18 @@ 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 * Flags (currently unused, will be zero)
*/ */
uint64_t flags; uint64_t flags;
@@ -1614,9 +1399,6 @@ typedef int (*ZT_PathCheckFunction)(
* 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 now Current clock in milliseconds * @param now Current clock in milliseconds
@@ -1707,15 +1489,6 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
*/ */
enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline); enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
/**
* Set node's relay policy
*
* @param node Node instance
* @param rp New relay policy
* @return OK(0) or error code
*/
enum ZT_ResultCode ZT_Node_setRelayPolicy(ZT_Node *node,enum ZT_RelayPolicy rp);
/** /**
* Join a network * Join a network
* *
@@ -2039,27 +1812,25 @@ void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned
void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs); void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs);
/** /**
* Set trusted paths * Do things in the background until Node dies
* *
* A trusted path is a physical network (network/bits) over which both * This function can be called from one or more background threads to process
* encryption and authentication can be skipped to improve performance. * certain tasks in the background to improve foreground performance. It will
* Each trusted path must have a non-zero unique ID that is the same across * not return until the Node is shut down. If threading is not enabled in
* all participating nodes. * this build it will return immediately and will do nothing.
* *
* We don't recommend using trusted paths at all unless you really *need* * This is completely optional. If this is never called, all processing is
* near-bare-metal performance. Even on a LAN authentication and encryption * done in the foreground in the various processXXXX() methods.
* are never a bad thing, and anything that introduces an "escape hatch"
* for encryption should be treated with the utmost care.
* *
* Calling with NULL pointers for networks and ids and a count of zero clears * This does NOT replace or eliminate the need to call the normal
* all trusted paths. * 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 * @param node Node instance
* @param networks Array of [count] networks
* @param ids Array of [count] corresponding non-zero path IDs (zero path IDs are ignored)
* @param count Number of trusted paths-- values greater than ZT_MAX_TRUSTED_PATHS are clipped
*/ */
void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count); void ZT_Node_backgroundThreadMain(ZT_Node *node);
/** /**
* Get ZeroTier One version * Get ZeroTier One version

View File

@@ -11,14 +11,15 @@ LOCAL_LDLIBS := -llog
# ZeroTierOne SDK source files # ZeroTierOne SDK source files
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
$(ZT1)/ext/lz4/lz4.c \ $(ZT1)/ext/lz4/lz4.c \
$(ZT1)/ext/json-parser/json.c \
$(ZT1)/ext/http-parser/http_parser.c \ $(ZT1)/ext/http-parser/http_parser.c \
$(ZT1)/node/C25519.cpp \ $(ZT1)/node/C25519.cpp \
$(ZT1)/node/Capability.cpp \
$(ZT1)/node/CertificateOfMembership.cpp \ $(ZT1)/node/CertificateOfMembership.cpp \
$(ZT1)/node/DeferredPackets.cpp \
$(ZT1)/node/Dictionary.cpp \
$(ZT1)/node/Identity.cpp \ $(ZT1)/node/Identity.cpp \
$(ZT1)/node/IncomingPacket.cpp \ $(ZT1)/node/IncomingPacket.cpp \
$(ZT1)/node/InetAddress.cpp \ $(ZT1)/node/InetAddress.cpp \
$(ZT1)/node/Membership.cpp \
$(ZT1)/node/Multicaster.cpp \ $(ZT1)/node/Multicaster.cpp \
$(ZT1)/node/Network.cpp \ $(ZT1)/node/Network.cpp \
$(ZT1)/node/NetworkConfig.cpp \ $(ZT1)/node/NetworkConfig.cpp \
@@ -32,7 +33,6 @@ LOCAL_SRC_FILES := \
$(ZT1)/node/SelfAwareness.cpp \ $(ZT1)/node/SelfAwareness.cpp \
$(ZT1)/node/SHA512.cpp \ $(ZT1)/node/SHA512.cpp \
$(ZT1)/node/Switch.cpp \ $(ZT1)/node/Switch.cpp \
$(ZT1)/node/Tag.cpp \
$(ZT1)/node/Topology.cpp \ $(ZT1)/node/Topology.cpp \
$(ZT1)/node/Utils.cpp \ $(ZT1)/node/Utils.cpp \
$(ZT1)/osdep/Http.cpp \ $(ZT1)/osdep/Http.cpp \

View File

@@ -1,5 +1,5 @@
# NDK_TOOLCHAIN_VERSION := clang3.5 NDK_TOOLCHAIN_VERSION := clang
APP_STL := gnustl_static APP_STL := c++_static
APP_CPPFLAGS := -O3 -fPIC -fPIE -Wall -fstack-protector -fexceptions -fno-strict-aliasing -Wno-deprecated-register -DZT_NO_TYPE_PUNNING=1 APP_CPPFLAGS := -O3 -fPIC -fPIE -fvectorize -Wall -fstack-protector -fexceptions -fno-strict-aliasing -Wno-deprecated-register -DZT_NO_TYPE_PUNNING=1
APP_PLATFORM := android-14 APP_PLATFORM := android-14
APP_ABI := all APP_ABI := all

View File

@@ -1,21 +1,3 @@
/*
* 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 "ZT_jniutils.h" #include "ZT_jniutils.h"
#include "ZT_jnilookup.h" #include "ZT_jnilookup.h"
#include <string> #include <string>
@@ -180,8 +162,8 @@ jobject createPeerRole(JNIEnv *env, ZT_PeerRole role)
case ZT_PEER_ROLE_LEAF: case ZT_PEER_ROLE_LEAF:
fieldName = "PEER_ROLE_LEAF"; fieldName = "PEER_ROLE_LEAF";
break; break;
case ZT_PEER_ROLE_UPSTREAM: case ZT_PEER_ROLE_RELAY:
fieldName = "PEER_ROLE_UPSTREAM"; fieldName = "PEER_ROLE_RELAY";
break; break;
case ZT_PEER_ROLE_ROOT: case ZT_PEER_ROLE_ROOT:
fieldName = "PEER_ROLE_ROOTS"; fieldName = "PEER_ROLE_ROOTS";
@@ -331,22 +313,13 @@ jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr)
return NULL; return NULL;
} }
jobject inetAddressObject = NULL; jobject inetAddressObject = newInetAddress(env, addr);
if(addr.ss_family != 0)
{
inetAddressObject = newInetAddress(env, addr);
if(env->ExceptionCheck() || inetAddressObject == NULL) if(env->ExceptionCheck() || inetAddressObject == NULL)
{ {
LOGE("Error creating new inet address"); LOGE("Error creating new inet address");
return NULL; return NULL;
} }
}
else
{
return NULL;
}
inetSocketAddress_constructor = lookup.findMethod( inetSocketAddress_constructor = lookup.findMethod(
inetSocketAddressClass, "<init>", "(Ljava/net/InetAddress;I)V"); inetSocketAddressClass, "<init>", "(Ljava/net/InetAddress;I)V");
@@ -377,9 +350,10 @@ jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr)
break; break;
default: default:
{ {
LOGE("ERROR: addr.ss_family is not set or unknown");
break; break;
} }
} };
jobject inetSocketAddressObject = env->NewObject(inetSocketAddressClass, inetSocketAddress_constructor, inetAddressObject, port); jobject inetSocketAddressObject = env->NewObject(inetSocketAddressClass, inetSocketAddress_constructor, inetAddressObject, port);
@@ -389,6 +363,51 @@ jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr)
return inetSocketAddressObject; return inetSocketAddressObject;
} }
jobject newMulticastGroup(JNIEnv *env, const ZT_MulticastGroup &mc)
{
jclass multicastGroupClass = NULL;
jmethodID multicastGroup_constructor = NULL;
jfieldID macField = NULL;
jfieldID adiField = NULL;
multicastGroupClass = lookup.findClass("com/zerotier/sdk/MulticastGroup");
if(env->ExceptionCheck() || multicastGroupClass == NULL)
{
return NULL;
}
multicastGroup_constructor = lookup.findMethod(
multicastGroupClass, "<init>", "()V");
if(env->ExceptionCheck() || multicastGroup_constructor == NULL)
{
return NULL;
}
jobject multicastGroupObj = env->NewObject(multicastGroupClass, multicastGroup_constructor);
if(env->ExceptionCheck() || multicastGroupObj == NULL)
{
return NULL;
}
macField = lookup.findField(multicastGroupClass, "mac", "J");
if(env->ExceptionCheck() || macField == NULL)
{
return NULL;
}
adiField = lookup.findField(multicastGroupClass, "adi", "J");
if(env->ExceptionCheck() || adiField == NULL)
{
return NULL;
}
env->SetLongField(multicastGroupObj, macField, mc.mac);
env->SetLongField(multicastGroupObj, adiField, mc.adi);
return multicastGroupObj;
}
jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp) jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp)
{ {
LOGV("newPeerPhysicalPath Called"); LOGV("newPeerPhysicalPath Called");
@@ -397,6 +416,7 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp)
jfieldID addressField = NULL; jfieldID addressField = NULL;
jfieldID lastSendField = NULL; jfieldID lastSendField = NULL;
jfieldID lastReceiveField = NULL; jfieldID lastReceiveField = NULL;
jfieldID activeField = NULL;
jfieldID preferredField = NULL; jfieldID preferredField = NULL;
jmethodID ppp_constructor = NULL; jmethodID ppp_constructor = NULL;
@@ -429,6 +449,13 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp)
return NULL; return NULL;
} }
activeField = lookup.findField(pppClass, "active", "Z");
if(env->ExceptionCheck() || activeField == NULL)
{
LOGE("Error finding active field");
return NULL;
}
preferredField = lookup.findField(pppClass, "preferred", "Z"); preferredField = lookup.findField(pppClass, "preferred", "Z");
if(env->ExceptionCheck() || preferredField == NULL) if(env->ExceptionCheck() || preferredField == NULL)
{ {
@@ -459,6 +486,7 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp)
env->SetObjectField(pppObject, addressField, addressObject); env->SetObjectField(pppObject, addressField, addressObject);
env->SetLongField(pppObject, lastSendField, ppp.lastSend); env->SetLongField(pppObject, lastSendField, ppp.lastSend);
env->SetLongField(pppObject, lastReceiveField, ppp.lastReceive); env->SetLongField(pppObject, lastReceiveField, ppp.lastReceive);
env->SetBooleanField(pppObject, activeField, ppp.active);
env->SetBooleanField(pppObject, preferredField, ppp.preferred); env->SetBooleanField(pppObject, preferredField, ppp.preferred);
if(env->ExceptionCheck()) { if(env->ExceptionCheck()) {
@@ -624,9 +652,10 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
jfieldID bridgeField = NULL; jfieldID bridgeField = NULL;
jfieldID broadcastEnabledField = NULL; jfieldID broadcastEnabledField = NULL;
jfieldID portErrorField = NULL; jfieldID portErrorField = NULL;
jfieldID enabledField = NULL;
jfieldID netconfRevisionField = NULL; jfieldID netconfRevisionField = NULL;
jfieldID multicastSubscriptionsField = NULL;
jfieldID assignedAddressesField = NULL; jfieldID assignedAddressesField = NULL;
jfieldID routesField = NULL;
vnetConfigClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkConfig"); vnetConfigClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkConfig");
if(vnetConfigClass == NULL) if(vnetConfigClass == NULL)
@@ -720,6 +749,13 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
return NULL; return NULL;
} }
enabledField = lookup.findField(vnetConfigClass, "enabled", "Z");
if(env->ExceptionCheck() || enabledField == NULL)
{
LOGE("Error getting enabled field");
return NULL;
}
netconfRevisionField = lookup.findField(vnetConfigClass, "netconfRevision", "J"); netconfRevisionField = lookup.findField(vnetConfigClass, "netconfRevision", "J");
if(env->ExceptionCheck() || netconfRevisionField == NULL) if(env->ExceptionCheck() || netconfRevisionField == NULL)
{ {
@@ -727,19 +763,17 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
return NULL; return NULL;
} }
assignedAddressesField = lookup.findField(vnetConfigClass, "assignedAddresses", multicastSubscriptionsField = lookup.findField(vnetConfigClass, "multicastSubscriptions", "[Lcom/zerotier/sdk/MulticastGroup;");
"[Ljava/net/InetSocketAddress;"); if(env->ExceptionCheck() || multicastSubscriptionsField == NULL)
if(env->ExceptionCheck() || assignedAddressesField == NULL)
{ {
LOGE("Error getting assignedAddresses field"); LOGE("Error getting multicastSubscriptions field");
return NULL; return NULL;
} }
routesField = lookup.findField(vnetConfigClass, "routes", assignedAddressesField = lookup.findField(vnetConfigClass, "assignedAddresses", "[Ljava/net/InetSocketAddress;");
"[Lcom/zerotier/sdk/VirtualNetworkRoute;"); if(env->ExceptionCheck() || assignedAddressesField == NULL)
if(env->ExceptionCheck() || routesField == NULL)
{ {
LOGE("Error getting routes field"); LOGE("Error getting assignedAddresses field");
return NULL; return NULL;
} }
@@ -770,8 +804,34 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
env->SetBooleanField(vnetConfigObj, dhcpField, vnetConfig.dhcp); env->SetBooleanField(vnetConfigObj, dhcpField, vnetConfig.dhcp);
env->SetBooleanField(vnetConfigObj, bridgeField, vnetConfig.bridge); env->SetBooleanField(vnetConfigObj, bridgeField, vnetConfig.bridge);
env->SetBooleanField(vnetConfigObj, broadcastEnabledField, vnetConfig.broadcastEnabled); env->SetBooleanField(vnetConfigObj, broadcastEnabledField, vnetConfig.broadcastEnabled);
env->SetBooleanField(vnetConfigObj, enabledField, vnetConfig.enabled);
env->SetIntField(vnetConfigObj, portErrorField, vnetConfig.portError); env->SetIntField(vnetConfigObj, portErrorField, vnetConfig.portError);
jclass multicastGroupClass = lookup.findClass("com/zerotier/sdk/MulticastGroup");
if(env->ExceptionCheck() || multicastGroupClass == NULL)
{
LOGE("Error finding MulticastGroup class");
return NULL;
}
jobjectArray mcastSubsArrayObj = env->NewObjectArray(
vnetConfig.multicastSubscriptionCount, multicastGroupClass, NULL);
if(env->ExceptionCheck() || mcastSubsArrayObj == NULL) {
LOGE("Error creating MulticastGroup[] array");
return NULL;
}
for(unsigned int i = 0; i < vnetConfig.multicastSubscriptionCount; ++i)
{
jobject mcastObj = newMulticastGroup(env, vnetConfig.multicastSubscriptions[i]);
env->SetObjectArrayElement(mcastSubsArrayObj, i, mcastObj);
if(env->ExceptionCheck())
{
LOGE("Error assigning MulticastGroup to array");
}
}
env->SetObjectField(vnetConfigObj, multicastSubscriptionsField, mcastSubsArrayObj);
jclass inetSocketAddressClass = lookup.findClass("java/net/InetSocketAddress"); jclass inetSocketAddressClass = lookup.findClass("java/net/InetSocketAddress");
if(env->ExceptionCheck() || inetSocketAddressClass == NULL) if(env->ExceptionCheck() || inetSocketAddressClass == NULL)
{ {
@@ -800,38 +860,10 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
env->SetObjectField(vnetConfigObj, assignedAddressesField, assignedAddrArrayObj); env->SetObjectField(vnetConfigObj, assignedAddressesField, assignedAddrArrayObj);
jclass virtualNetworkRouteClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkRoute");
if(env->ExceptionCheck() || virtualNetworkRouteClass == NULL)
{
LOGE("Error finding VirtualNetworkRoute class");
return NULL;
}
jobjectArray routesArrayObj = env->NewObjectArray(
vnetConfig.routeCount, virtualNetworkRouteClass, NULL);
if(env->ExceptionCheck() || routesArrayObj == NULL)
{
LOGE("Error creating VirtualNetworkRoute[] array");
return NULL;
}
for(unsigned int i = 0; i < vnetConfig.routeCount; ++i)
{
jobject routeObj = newVirtualNetworkRoute(env, vnetConfig.routes[i]);
env->SetObjectArrayElement(routesArrayObj, i, routeObj);
if(env->ExceptionCheck())
{
LOGE("Error assigning VirtualNetworkRoute to array");
return NULL;
}
}
env->SetObjectField(vnetConfigObj, routesField, routesArrayObj);
return vnetConfigObj; return vnetConfigObj;
} }
jobject newVersion(JNIEnv *env, int major, int minor, int rev) jobject newVersion(JNIEnv *env, int major, int minor, int rev, long featureFlags)
{ {
// create a com.zerotier.sdk.Version object // create a com.zerotier.sdk.Version object
jclass versionClass = NULL; jclass versionClass = NULL;
@@ -860,6 +892,7 @@ jobject newVersion(JNIEnv *env, int major, int minor, int rev)
jfieldID majorField = NULL; jfieldID majorField = NULL;
jfieldID minorField = NULL; jfieldID minorField = NULL;
jfieldID revisionField = NULL; jfieldID revisionField = NULL;
jfieldID featureFlagsField = NULL;
majorField = lookup.findField(versionClass, "major", "I"); majorField = lookup.findField(versionClass, "major", "I");
if(env->ExceptionCheck() || majorField == NULL) if(env->ExceptionCheck() || majorField == NULL)
@@ -879,79 +912,20 @@ jobject newVersion(JNIEnv *env, int major, int minor, int rev)
return NULL; return NULL;
} }
featureFlagsField = lookup.findField(versionClass, "featureFlags", "J");
if(env->ExceptionCheck() || featureFlagsField == NULL)
{
return NULL;
}
env->SetIntField(versionObj, majorField, (jint)major); env->SetIntField(versionObj, majorField, (jint)major);
env->SetIntField(versionObj, minorField, (jint)minor); env->SetIntField(versionObj, minorField, (jint)minor);
env->SetIntField(versionObj, revisionField, (jint)rev); env->SetIntField(versionObj, revisionField, (jint)rev);
env->SetLongField(versionObj, featureFlagsField, (jlong)featureFlags);
return versionObj; return versionObj;
} }
jobject newVirtualNetworkRoute(JNIEnv *env, const ZT_VirtualNetworkRoute &route)
{
jclass virtualNetworkRouteClass = NULL;
jmethodID routeConstructor = NULL;
virtualNetworkRouteClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkRoute");
if(env->ExceptionCheck() || virtualNetworkRouteClass == NULL)
{
return NULL;
}
routeConstructor = lookup.findMethod(virtualNetworkRouteClass, "<init>", "()V");
if(env->ExceptionCheck() || routeConstructor == NULL)
{
return NULL;
}
jobject routeObj = env->NewObject(virtualNetworkRouteClass, routeConstructor);
if(env->ExceptionCheck() || routeObj == NULL)
{
return NULL;
}
jfieldID targetField = NULL;
jfieldID viaField = NULL;
jfieldID flagsField = NULL;
jfieldID metricField = NULL;
targetField = lookup.findField(virtualNetworkRouteClass, "target",
"Ljava/net/InetSocketAddress;");
if(env->ExceptionCheck() || targetField == NULL)
{
return NULL;
}
viaField = lookup.findField(virtualNetworkRouteClass, "via",
"Ljava/net/InetSocketAddress;");
if(env->ExceptionCheck() || targetField == NULL)
{
return NULL;
}
flagsField = lookup.findField(virtualNetworkRouteClass, "flags", "I");
if(env->ExceptionCheck() || flagsField == NULL)
{
return NULL;
}
metricField = lookup.findField(virtualNetworkRouteClass, "metric", "I");
if(env->ExceptionCheck() || metricField == NULL)
{
return NULL;
}
jobject targetObj = newInetSocketAddress(env, route.target);
jobject viaObj = newInetSocketAddress(env, route.via);
env->SetObjectField(routeObj, targetField, targetObj);
env->SetObjectField(routeObj, viaField, viaObj);
env->SetIntField(routeObj, flagsField, (jint)route.flags);
env->SetIntField(routeObj, metricField, (jint)route.metric);
return routeObj;
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,21 +1,3 @@
/*
* 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_jniutils_h_ #ifndef ZT_jniutils_h_
#define ZT_jniutils_h_ #define ZT_jniutils_h_
#include <stdio.h> #include <stdio.h>
@@ -58,9 +40,7 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp);
jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &config); jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &config);
jobject newVersion(JNIEnv *env, int major, int minor, int rev); jobject newVersion(JNIEnv *env, int major, int minor, int rev, long featureFlags);
jobject newVirtualNetworkRoute(JNIEnv *env, const ZT_VirtualNetworkRoute &route);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -1234,10 +1234,11 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_version(
int major = 0; int major = 0;
int minor = 0; int minor = 0;
int revision = 0; int revision = 0;
unsigned long featureFlags = 0;
ZT_version(&major, &minor, &revision); ZT_version(&major, &minor, &revision, &featureFlags);
return newVersion(env, major, minor, revision); return newVersion(env, major, minor, revision, featureFlags);
} }
/* /*

View File

@@ -37,6 +37,7 @@ public final class PeerPhysicalPath {
private long lastSend; private long lastSend;
private long lastReceive; private long lastReceive;
private boolean fixed; private boolean fixed;
private boolean active;
private boolean preferred; private boolean preferred;
private PeerPhysicalPath() {} private PeerPhysicalPath() {}
@@ -69,6 +70,13 @@ public final class PeerPhysicalPath {
return fixed; return fixed;
} }
/**
* Is path active?
*/
public final boolean isActive() {
return active;
}
/** /**
* Is path preferred? * Is path preferred?
*/ */

View File

@@ -34,9 +34,9 @@ public enum PeerRole {
PEER_ROLE_LEAF, PEER_ROLE_LEAF,
/** /**
* upstream node * relay node
*/ */
PEER_ROLE_UPSTREAM, PEER_ROLE_RELAY,
/** /**
* root server * root server

View File

@@ -33,4 +33,5 @@ public final class Version {
public int major = 0; public int major = 0;
public int minor = 0; public int minor = 0;
public int revision = 0; public int revision = 0;
public long featureFlags = 0;
} }

View File

@@ -49,8 +49,8 @@ public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConf
private int portError; private int portError;
private boolean enabled; private boolean enabled;
private long netconfRevision; private long netconfRevision;
private MulticastGroup[] multicastSubscriptions;
private InetSocketAddress[] assignedAddresses; private InetSocketAddress[] assignedAddresses;
private VirtualNetworkRoute[] routes;
private VirtualNetworkConfig() { private VirtualNetworkConfig() {
@@ -61,24 +61,13 @@ public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConf
if(assignedAddresses.length == cfg.assignedAddresses.length) { if(assignedAddresses.length == cfg.assignedAddresses.length) {
for(int i = 0; i < assignedAddresses.length; ++i) { for(int i = 0; i < assignedAddresses.length; ++i) {
if(!assignedAddresses[i].equals(cfg.assignedAddresses[i])) { if(!assignedAddresses[i].equals(cfg.assignedAddresses[i])) {
aaEqual = false; return false;
} }
} }
} else { } else {
aaEqual = false; aaEqual = false;
} }
boolean routesEqual = true;
if(routes.length == cfg.routes.length) {
for (int i = 0; i < routes.length; ++i) {
if (!routes[i].equals(cfg.routes[i])) {
routesEqual = false;
}
}
} else {
routesEqual = false;
}
return nwid == cfg.nwid && return nwid == cfg.nwid &&
mac == cfg.mac && mac == cfg.mac &&
name.equals(cfg.name) && name.equals(cfg.name) &&
@@ -90,7 +79,7 @@ public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConf
broadcastEnabled == cfg.broadcastEnabled && broadcastEnabled == cfg.broadcastEnabled &&
portError == cfg.portError && portError == cfg.portError &&
enabled == cfg.enabled && enabled == cfg.enabled &&
aaEqual && routesEqual; aaEqual;
} }
public int compareTo(VirtualNetworkConfig cfg) { public int compareTo(VirtualNetworkConfig cfg) {
@@ -178,6 +167,13 @@ public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConf
return portError; return portError;
} }
/**
* Is this network enabled? If not, all frames to/from are dropped.
*/
public final boolean isEnabled() {
return enabled;
}
/** /**
* Network config revision as reported by netconf master * Network config revision as reported by netconf master
* *
@@ -187,6 +183,13 @@ public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConf
return netconfRevision; return netconfRevision;
} }
/**
* Multicast group subscriptions
*/
public final MulticastGroup[] multicastSubscriptions() {
return multicastSubscriptions;
}
/** /**
* ZeroTier-assigned addresses (in {@link java.net.InetSocketAddress} objects) * ZeroTier-assigned addresses (in {@link java.net.InetSocketAddress} objects)
* *
@@ -200,11 +203,4 @@ public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConf
public final InetSocketAddress[] assignedAddresses() { public final InetSocketAddress[] assignedAddresses() {
return assignedAddresses; return assignedAddresses;
} }
/**
* ZeroTier-assigned routes (in {@link com.zerotier.sdk.VirtualNetworkRoute} objects)
*
* @return
*/
public final VirtualNetworkRoute[] routes() { return routes; }
} }

View File

@@ -6,7 +6,7 @@ DEFS=
LIBS= LIBS=
include objects.mk include objects.mk
OBJS+=osdep/BSDEthernetTap.o ext/lz4/lz4.o ext/http-parser/http_parser.o OBJS+=osdep/BSDEthernetTap.o
# "make official" is a shortcut for this # "make official" is a shortcut for this
ifeq ($(ZT_OFFICIAL_RELEASE),1) ifeq ($(ZT_OFFICIAL_RELEASE),1)

View File

@@ -9,16 +9,18 @@
# #
# Targets # Targets
# one: zerotier-one and symlinks (cli and idtool) # one: zerotier-one and symlinks (cli and idtool)
# manpages: builds manpages, requires 'ronn' or nodeJS (will use either) # doc: builds manpages, requires rst2man somewhere in PATH
# all: builds 'one' and 'manpages' # all: builds 'one'
# selftest: zerotier-selftest # selftest: zerotier-selftest
# debug: builds 'one' and 'selftest' with tracing and debug flags # debug: builds 'one' and 'selftest' with tracing and debug flags
# installer: builds installers and packages (RPM/DEB/etc.) if possible
# official: cleans and then builds 'one', 'installer', and 'doc'
# clean: removes all built files, objects, other trash # 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
# #
GENERATED_FILES :=
DOC_DIR = doc
# 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)
@@ -31,85 +33,67 @@ endif
#UNAME_M=$(shell $(CC) -dumpmachine | cut -d '-' -f 1) #UNAME_M=$(shell $(CC) -dumpmachine | cut -d '-' -f 1)
INCLUDES?= INCLUDES?=
DEFS?=-D_FORTIFY_SOURCE=2 DEFS?=
LDLIBS?= LDLIBS?=
DESTDIR?=
include objects.mk include objects.mk
# On Linux we auto-detect the presence of some libraries and if present we ifeq ($(ZT_OFFICIAL_RELEASE),1)
# link against the system version. This works with our package build images. DEFS+=-DZT_OFFICIAL_RELEASE
ifeq ($(wildcard /usr/include/lz4.h),) ZT_USE_MINIUPNPC=1
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
else
LDLIBS+=-lhttp_parser
DEFS+=-DZT_USE_SYSTEM_HTTP_PARSER
endif endif
ifeq ($(ZT_USE_MINIUPNPC),1) ifeq ($(ZT_USE_MINIUPNPC),1)
OBJS+=osdep/PortMapper.o DEFS+=-DZT_USE_MINIUPNPC -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=\"1.9\" -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
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)
ifeq ($(MINIUPNPC_IS_NEW_ENOUGH),1)
DEFS+=-DZT_USE_SYSTEM_MINIUPNPC
LDLIBS+=-lminiupnpc
else
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
endif endif
# Auto-detect libnatpmp ifeq ($(ZT_ENABLE_NETWORK_CONTROLLER),1)
ifeq ($(wildcard /usr/include/natpmp.h),) DEFS+=-DZT_ENABLE_NETWORK_CONTROLLER
OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o LDLIBS+=-L/usr/local/lib -lsqlite3
else OBJS+=controller/SqliteNetworkController.o
LDLIBS+=-lnatpmp
DEFS+=-DZT_USE_SYSTEM_NATPMP
endif
endif endif
ifeq ($(ZT_ENABLE_CLUSTER),1) ifeq ($(ZT_ENABLE_CLUSTER),1)
DEFS+=-DZT_ENABLE_CLUSTER DEFS+=-DZT_ENABLE_CLUSTER
endif endif
ifeq ($(ZT_TRACE),1)
DEFS+=-DZT_TRACE
endif
ifeq ($(ZT_DEBUG),1) ifeq ($(ZT_DEBUG),1)
DEFS+=-DZT_TRACE DEFS+=-DZT_TRACE
override CFLAGS+=-Wall -g -O -pthread $(INCLUDES) $(DEFS) CFLAGS+=-Wall -g -pthread $(INCLUDES) $(DEFS)
override CXXFLAGS+=-Wall -g -O -std=c++11 -pthread $(INCLUDES) $(DEFS) CXXFLAGS+=-Wall -g -pthread $(INCLUDES) $(DEFS)
override LDFLAGS+= LDFLAGS=-ldl
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) ext/lz4/lz4.o 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 CFLAGS?=-O3 -fstack-protector
override CFLAGS+=-Wall -fPIE -pthread $(INCLUDES) -DNDEBUG $(DEFS) CFLAGS+=-Wall -fPIE -fvisibility=hidden -pthread $(INCLUDES) -DNDEBUG $(DEFS)
CXXFLAGS?=-O3 -fstack-protector CXXFLAGS?=-O3 -fstack-protector
override CXXFLAGS+=-Wall -Wno-unused-result -Wreorder -fPIE -std=c++11 -pthread $(INCLUDES) -DNDEBUG $(DEFS) CXXFLAGS+=-Wall -Wreorder -fPIE -fvisibility=hidden -fno-rtti -pthread $(INCLUDES) -DNDEBUG $(DEFS)
override LDFLAGS+=-pie -Wl,-z,relro,-z,now LDFLAGS=-ldl -pie -Wl,-z,relro,-z,now
STRIP?=strip STRIP?=strip
STRIP+=--strip-all STRIP+=--strip-all
endif endif
ifeq ($(ZT_TRACE),1)
DEFS+=-DZT_TRACE
endif
# Debug output for Network Containers
# Specific levels can be controlled in netcon/common.inc.c
ifeq ($(SDK_DEBUG),1)
DEFS+=-DSDK_DEBUG
endif
# Uncomment for gprof profile build # Uncomment for gprof profile build
#CFLAGS=-Wall -g -pg -pthread $(INCLUDES) $(DEFS) #CFLAGS=-Wall -g -pg -pthread $(INCLUDES) $(DEFS)
#CXXFLAGS=-Wall -g -pg -pthread $(INCLUDES) $(DEFS) #CXXFLAGS=-Wall -g -pg -pthread $(INCLUDES) $(DEFS)
#LDFLAGS= #LDFLAGS=
#STRIP=echo #STRIP=echo
all: one manpages 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)
@@ -117,82 +101,47 @@ one: $(OBJS) service/OneService.o one.o osdep/LinuxEthernetTap.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
netcon: $(OBJS) FORCE
rm -f *.o
# Need to selectively rebuild one.cpp and OneService.cpp with ZT_SERVICE_NETCON and ZT_ONE_NO_ROOT_CHECK defined, and also NetconEthernetTap
$(CXX) $(CXXFLAGS) $(LDFLAGS) -DZT_SDK -DZT_ONE_NO_ROOT_CHECK -Iext/lwip/src/include -Iext/lwip/src/include/ipv4 -Iext/lwip/src/include/ipv6 -o zerotier-netcon-service $(OBJS) service/OneService.cpp netcon/NetconEthernetTap.cpp netcon/NetconProxy.cpp one.cpp -x c netcon/NetconRPC.c $(LDLIBS) -ldl
# Build netcon/liblwip.so which must be placed in ZT home for zerotier-netcon-service to work
cd netcon ; make -f make-liblwip.mk
# Use gcc not clang to build standalone intercept library since gcc is typically used for libc and we want to ensure maximal ABI compatibility
#cd netcon ; gcc $(DEFS) -g -O2 -Wall -std=c99 -fPIC -DVERBOSE -D_GNU_SOURCE -DNETCON_INTERCEPT -I. -nostdlib -shared -o libztapi.so zt_api.c RPC.c -ldl
cd netcon ; gcc $(DEFS) -g -O2 -Wall -std=c99 -fPIC -DVERBOSE -D_GNU_SOURCE -DNETCON_INTERCEPT -I. -nostdlib -shared -o libztintercept.so NetconSockets.c Intercept.c NetconDebug.c NetconRPC.c -ldl
#cd netcon ; gcc $(DEFS) -g -O2 -Wall -std=c99 -fPIC -DVERBOSE -D_GNU_SOURCE -DNETCON_INTERCEPT -I. -nostdlib -shared -o libztkq.so zt_api.c kq.c Intercept.c RPC.c -ldl
#cd netcon ; gcc $(DEFS) -g -O2 -Wall -std=c99 -fPIC -DVERBOSE -D_GNU_SOURCE -DNETCON_INTERCEPT -I. -o ztproxy ztproxy.c zt_api.c RPC.c -ldl
#cp netcon/ztproxy ztproxy
#cp netcon/libztapi.so libztapi.so
#cp netcon/libztkq.so libztkq.so
cp netcon/libztintercept.so libztintercept.so
ln -sf zerotier-netcon-service zerotier-cli
ln -sf zerotier-netcon-service zerotier-idtool
selftest: $(OBJS) selftest.o selftest: $(OBJS) selftest.o
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LDLIBS) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LDLIBS)
$(STRIP) zerotier-selftest $(STRIP) zerotier-selftest
manpages: FORCE installer: one FORCE
cd doc ; ./build.sh ./ext/installfiles/linux/buildinstaller.sh
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 ${GENERATED_FILES} *.so *.o netcon/*.a 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 zerotier-netcon-service build-* ZeroTierOneInstaller-* *.deb *.rpm .depend netcon/.depend
find netcon -type f \( -name '*.o' -o -name '*.so' -o -name '*.1.0' -o -name 'zerotier-one' -o -name 'zerotier-cli' -o -name 'zerotier-netcon-service' \) -delete
distclean: clean find netcon/tests/docker -name "zerotier-intercept" -type f -delete
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
debug: FORCE debug: FORCE
make ZT_DEBUG=1 one make ZT_DEBUG=1 one
make ZT_DEBUG=1 selftest make ZT_DEBUG=1 selftest
# Note: keep the symlinks in /var/lib/zerotier-one to the binaries since these official: FORCE
# provide backward compatibility with old releases where the binaries actually make ZT_OFFICIAL_RELEASE=1 clean
# lived here. Folks got scripts. make -j 4 ZT_OFFICIAL_RELEASE=1 one
make ZT_OFFICIAL_RELEASE=1 installer
make ZT_OFFICIAL_RELEASE=1 doc
install: FORCE # Includes 'doc' target
mkdir -p $(DESTDIR)/usr/sbin include ${DOC_DIR}/module.mk
rm -f $(DESTDIR)/usr/sbin/zerotier-one
cp -f zerotier-one $(DESTDIR)/usr/sbin/zerotier-one
rm -f $(DESTDIR)/usr/sbin/zerotier-cli
rm -f $(DESTDIR)/usr/sbin/zerotier-idtool
ln -s zerotier-one $(DESTDIR)/usr/sbin/zerotier-cli
ln -s zerotier-one $(DESTDIR)/usr/sbin/zerotier-idtool
mkdir -p $(DESTDIR)/var/lib/zerotier-one
rm -f $(DESTDIR)/var/lib/zerotier-one/zerotier-one
rm -f $(DESTDIR)/var/lib/zerotier-one/zerotier-cli
rm -f $(DESTDIR)/var/lib/zerotier-one/zerotier-idtool
ln -s ../../../usr/sbin/zerotier-one $(DESTDIR)/var/lib/zerotier-one/zerotier-one
ln -s ../../../usr/sbin/zerotier-one $(DESTDIR)/var/lib/zerotier-one/zerotier-cli
ln -s ../../../usr/sbin/zerotier-one $(DESTDIR)/var/lib/zerotier-one/zerotier-idtool
mkdir -p $(DESTDIR)/usr/share/man/man8
rm -f $(DESTDIR)/usr/share/man/man8/zerotier-one.8.gz
cat doc/zerotier-one.8 | gzip -9 >$(DESTDIR)/usr/share/man/man8/zerotier-one.8.gz
mkdir -p $(DESTDIR)/usr/share/man/man1
rm -f $(DESTDIR)/usr/share/man/man1/zerotier-idtool.1.gz
rm -f $(DESTDIR)/usr/share/man/man1/zerotier-cli.1.gz
cat doc/zerotier-cli.1 | gzip -9 >$(DESTDIR)/usr/share/man/man1/zerotier-cli.1.gz
cat doc/zerotier-idtool.1 | gzip -9 >$(DESTDIR)/usr/share/man/man1/zerotier-idtool.1.gz
# Uninstall preserves identity.public and identity.secret since the user might
# want to save these. These are your ZeroTier address.
uninstall: FORCE
rm -f $(DESTDIR)/var/lib/zerotier-one/zerotier-one
rm -f $(DESTDIR)/var/lib/zerotier-one/zerotier-cli
rm -f $(DESTDIR)/var/lib/zerotier-one/zerotier-idtool
rm -f $(DESTDIR)/usr/sbin/zerotier-cli
rm -f $(DESTDIR)/usr/sbin/zerotier-idtool
rm -f $(DESTDIR)/usr/sbin/zerotier-one
rm -rf $(DESTDIR)/var/lib/zerotier-one/iddb.d
rm -rf $(DESTDIR)/var/lib/zerotier-one/updates.d
rm -rf $(DESTDIR)/var/lib/zerotier-one/networks.d
rm -f $(DESTDIR)/var/lib/zerotier-one/zerotier-one.port
rm -f $(DESTDIR)/usr/share/man/man8/zerotier-one.8.gz
rm -f $(DESTDIR)/usr/share/man/man1/zerotier-idtool.1.gz
rm -f $(DESTDIR)/usr/share/man/man1/zerotier-cli.1.gz
# These are just for convenience for building Linux packages
debian: distclean
debuild -I -i -us -uc
redhat: distclean
rpmbuild -ba zerotier-one.spec
FORCE: FORCE:

View File

@@ -11,7 +11,7 @@ LIBS=
ARCH_FLAGS=-arch x86_64 ARCH_FLAGS=-arch x86_64
include objects.mk include objects.mk
OBJS+=osdep/OSXEthernetTap.o ext/lz4/lz4.o ext/http-parser/http_parser.o OBJS+=osdep/OSXEthernetTap.o
# 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
@@ -19,7 +19,7 @@ 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 # Build with libminiupnpc by default for Mac
ZT_USE_MINIUPNPC?=1 ZT_USE_MINIUPNPC?=1
# For internal use only -- signs everything with ZeroTier's developer cert # For internal use only -- signs everything with ZeroTier's developer cert
@@ -32,6 +32,7 @@ ifeq ($(ZT_OFFICIAL_RELEASE),1)
CODESIGN_INSTALLER_CERT="Developer ID Installer: ZeroTier Networks LLC (8ZD9JUCZ4V)" CODESIGN_INSTALLER_CERT="Developer ID Installer: ZeroTier Networks LLC (8ZD9JUCZ4V)"
endif endif
# Build with ZT_ENABLE_CLUSTER=1 to build with cluster support
ifeq ($(ZT_ENABLE_CLUSTER),1) ifeq ($(ZT_ENABLE_CLUSTER),1)
DEFS+=-DZT_ENABLE_CLUSTER DEFS+=-DZT_ENABLE_CLUSTER
endif endif
@@ -41,10 +42,17 @@ ifeq ($(ZT_AUTO_UPDATE),1)
endif endif
ifeq ($(ZT_USE_MINIUPNPC),1) 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=\"1.9\" -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 endif
# Build with ZT_ENABLE_NETWORK_CONTROLLER=1 to build with the Sqlite network controller
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)
DEFS+=-DZT_TRACE DEFS+=-DZT_TRACE
@@ -54,12 +62,18 @@ ifeq ($(ZT_DEBUG),1)
# 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) ext/lz4/lz4.o 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
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) -mmacosx-version-min=10.7 -std=c++11 -stdlib=libc++ # Debug output for Network Containers
# Specific levels can be controlled in netcon/common.inc.c
ifeq ($(SDK_DEBUG),1)
DEFS+=-DSDK_DEBUG
endif
CXXFLAGS=$(CFLAGS) -fno-rtti
all: one all: one
@@ -71,9 +85,21 @@ one: $(OBJS) service/OneService.o one.o
$(CODESIGN) -f -s $(CODESIGN_APP_CERT) zerotier-one $(CODESIGN) -f -s $(CODESIGN_APP_CERT) zerotier-one
$(CODESIGN) -vvv zerotier-one $(CODESIGN) -vvv zerotier-one
cli: FORCE netcon: $(OBJS)
$(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 rm -f *.o
$(STRIP) zerotier # Need to selectively rebuild one.cpp and OneService.cpp with ZT_SERVICE_NETCON and ZT_ONE_NO_ROOT_CHECK defined, and also NetconEthernetTap
$(CXX) $(CXXFLAGS) $(LDFLAGS) -DZT_SDK -DZT_ONE_NO_ROOT_CHECK -Iext/lwip/src/include -Iext/lwip/src/include/ipv4 -Iext/lwip/src/include/ipv6 -o zerotier-netcon-service $(OBJS) service/OneService.cpp netcon/NetconEthernetTap.cpp netcon/NetconProxy.cpp one.cpp -x c netcon/NetconRPC.c $(LDLIBS) -ldl
# Build netcon/liblwip.so which must be placed in ZT home for zerotier-netcon-service to work
cd netcon ; make -f make-liblwip.mk
# Use gcc not clang to build standalone intercept library since gcc is typically used for libc and we want to ensure maximal ABI compatibility
#cd netcon ; gcc $(DEFS) -O2 -Wall -std=c99 -fPIC -fno-common -dynamiclib -flat_namespace -DVERBOSE -D_GNU_SOURCE -DNETCON_INTERCEPT -I. -nostdlib -shared -o libztapi.so zt_api.c common.c RPC.c -ldl
cd netcon ; gcc $(DEFS) -O2 -Wall -std=c99 -fPIC -fno-common -dynamiclib -flat_namespace -DVERBOSE -D_GNU_SOURCE -DNETCON_INTERCEPT -I. -nostdlib -shared -o libztintercept.so NetconSockets.c Intercept.c NetconDebug.c NetconRPC.c -ldl
#cd netcon ; gcc $(DEFS) -O2 -Wall -std=c99 -fPIC -fno-common -dynamiclib -flat_namespace -DVERBOSE -D_GNU_SOURCE -DNETCON_INTERCEPT -I. -nostdlib -shared -o libztkq.so zt_api.c kq.c Intercept.c common.c RPC.c -ldl
cp netcon/libztintercept.so libztintercept.so
#cp netcon/libztapi.so libztapi.so
#cp netcon/libztkq.so libztkq.so
ln -sf zerotier-netcon-service zerotier-cli
ln -sf zerotier-netcon-service zerotier-idtool
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)
@@ -86,17 +112,17 @@ mac-dist-pkg: FORCE
$(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
# For ZeroTier, Inc. to build official signed packages # For internal use only
official: FORCE official: FORCE
make clean make ZT_OFFICIAL_RELEASE=1 clean
make ZT_OFFICIAL_RELEASE=1 -j 4 one make -j 4 ZT_OFFICIAL_RELEASE=1
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 # Remove junk generated by Android builds
rm -rf netcon/Android/*.o.d
distclean: clean rm -rf netcon/Netcon-Android/proj/app/build/intermediates
rm -rf doc/node_modules rm -rf netcon/*.o netcon/*.so *.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) netcon/zerotier-intercept zerotier-netcon-service ztproxy libztapi.so libztkq.so libztintercept.so zerotier-one zerotier-idtool zerotier-selftest zerotier-cli ZeroTierOneInstaller-* mkworld
# 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

View File

@@ -20,9 +20,11 @@
#define ZT_ATOMICCOUNTER_HPP #define ZT_ATOMICCOUNTER_HPP
#include "Constants.hpp" #include "Constants.hpp"
#include "Mutex.hpp"
#include "NonCopyable.hpp" #include "NonCopyable.hpp"
#ifndef __GNUC__ #ifdef __WINDOWS__
// <atomic> will replace this whole class eventually once it's ubiquitous
#include <atomic> #include <atomic>
#endif #endif
@@ -34,34 +36,75 @@ 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 __GNUC__ #ifdef __WINDOWS__
int _v;
#else
std::atomic_int _v; std::atomic_int _v;
#else
int _v;
#ifndef __GNUC__
#warning Neither __WINDOWS__ nor __GNUC__ so AtomicCounter using Mutex
Mutex _l;
#endif
#endif #endif
}; };

View File

@@ -17,10 +17,6 @@
*/ */
#include "CertificateOfMembership.hpp" #include "CertificateOfMembership.hpp"
#include "RuntimeEnvironment.hpp"
#include "Topology.hpp"
#include "Switch.hpp"
#include "Network.hpp"
namespace ZeroTier { namespace ZeroTier {
@@ -156,9 +152,6 @@ 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.
@@ -189,7 +182,7 @@ bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) c
bool CertificateOfMembership::sign(const Identity &with) bool CertificateOfMembership::sign(const Identity &with)
{ {
uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3]; uint64_t *const buf = new uint64_t[_qualifierCount * 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);
@@ -200,32 +193,38 @@ 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;
} }
} }
int CertificateOfMembership::verify(const RuntimeEnvironment *RR) const bool CertificateOfMembership::verify(const Identity &id) const
{ {
if ((!_signedBy)||(_signedBy != Network::controllerFor(networkId()))||(_qualifierCount > ZT_NETWORK_COM_MAX_QUALIFIERS)) if (!_signedBy)
return -1; return false;
if (id.address() != _signedBy)
return false;
const Identity id(RR->topology->getIdentity(_signedBy)); uint64_t *const buf = new uint64_t[_qualifierCount * 3];
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

View File

@@ -34,14 +34,22 @@
#include "Utils.hpp" #include "Utils.hpp"
/** /**
* Maximum number of qualifiers allowed in a COM (absolute max: 65535) * Default window of time for certificate agreement
*
* 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_MAX_QUALIFIERS 8 #define ZT_NETWORK_COM_DEFAULT_REVISION_MAX_DELTA (ZT_NETWORK_AUTOCONF_DELAY * 5)
/**
* 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
* *
@@ -71,11 +79,22 @@ class RuntimeEnvironment;
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 1024 are reserved for use as standard IDs. Others are available * IDs below 65536 should be considered reserved for future global
* for user-defined use. * assignment here.
* *
* 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.
@@ -83,27 +102,36 @@ public:
enum ReservedId enum ReservedId
{ {
/** /**
* Timestamp of certificate * Revision number 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_TIMESTAMP = 0, COM_RESERVED_ID_REVISION = 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 of membership * Create an empty certificate
*/ */
CertificateOfMembership() CertificateOfMembership() :
_qualifierCount(0)
{ {
memset(this,0,sizeof(CertificateOfMembership)); memset(_signature.data,0,_signature.size());
} }
CertificateOfMembership(const CertificateOfMembership &c) CertificateOfMembership(const CertificateOfMembership &c)
@@ -114,16 +142,16 @@ public:
/** /**
* Create from required fields common to all networks * Create from required fields common to all networks
* *
* @param timestamp Timestamp of certificate * @param revision Revision number 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 timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo) CertificateOfMembership(uint64_t revision,uint64_t revisionMaxDelta,uint64_t nwid,const Address &issuedTo)
{ {
_qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP; _qualifiers[0].id = COM_RESERVED_ID_REVISION;
_qualifiers[0].value = timestamp; _qualifiers[0].value = revision;
_qualifiers[0].maxDelta = timestampMaxDelta; _qualifiers[0].maxDelta = revisionMaxDelta;
_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;
@@ -140,6 +168,22 @@ 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
* *
@@ -158,15 +202,45 @@ public:
inline operator bool() const throw() { return (_qualifierCount != 0); } inline operator bool() const throw() { return (_qualifierCount != 0); }
/** /**
* @return Timestamp for this cert and maximum delta for timestamp * Check for presence of all required fields common to all networks
*
* @return True if all required fields are present
*/ */
inline std::pair<uint64_t,uint64_t> timestamp() const inline bool hasRequiredFields() 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_TIMESTAMP) if (_qualifiers[i].id == COM_RESERVED_ID_REVISION)
return std::pair<uint64_t,uint64_t>(_qualifiers[i].value,_qualifiers[i].maxDelta); return _qualifiers[i].maxDelta;
} }
return std::pair<uint64_t,uint64_t>(0ULL,0ULL); return 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;
} }
/** /**
@@ -221,6 +295,7 @@ public:
* @param s String to deserialize * @param s String to deserialize
*/ */
void fromString(const char *s); void fromString(const char *s);
inline void fromString(const std::string &s) { fromString(s.c_str()); }
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF #endif // ZT_SUPPORT_OLD_STYLE_NETCONF
/** /**
@@ -247,12 +322,12 @@ public:
bool sign(const Identity &with); bool sign(const Identity &with);
/** /**
* Verify this COM and its signature * Verify certificate against an identity
* *
* @param RR Runtime environment for looking up peers * @param id Identity to verify against
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential * @return True if certificate is signed by this identity and verification was successful
*/ */
int verify(const RuntimeEnvironment *RR) const; bool verify(const Identity &id) const;
/** /**
* @return True if signed * @return True if signed
@@ -267,7 +342,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((uint8_t)1); b.append((unsigned char)COM_UINT64_ED25519);
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);
@@ -287,8 +362,8 @@ public:
_qualifierCount = 0; _qualifierCount = 0;
_signedBy.zero(); _signedBy.zero();
if (b[p++] != 1) if (b[p++] != COM_UINT64_ED25519)
throw std::invalid_argument("invalid object"); throw std::invalid_argument("invalid type");
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;

View File

@@ -361,7 +361,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->hasLocalClusterOptimalPath(RR->node->now())) ) { if ( (peer) && (peer->hasClusterOptimalPath(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);
@@ -455,7 +455,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); RR->sw->send(rendezvousForLocal,true,0);
} }
} }
} break; } break;
@@ -466,7 +466,7 @@ 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); RR->sw->send(outp,true,0);
//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;
} }
@@ -719,9 +719,7 @@ 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) {
@@ -733,9 +731,7 @@ 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;
} }
} }

View File

@@ -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
*/ */
@@ -226,21 +226,11 @@
*/ */
#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
* *
@@ -249,44 +239,30 @@
#define ZT_MULTICAST_TRANSMIT_TIMEOUT 5000 #define ZT_MULTICAST_TRANSMIT_TIMEOUT 5000
/** /**
* Delay between checks of peer pings, etc., and also related housekeeping tasks * Default maximum number of peers to address with a single multicast (if unspecified in network config)
*/ */
#define ZT_PING_CHECK_INVERVAL 5000 #define ZT_MULTICAST_DEFAULT_LIMIT 32
/** /**
* How frequently to send heartbeats over in-use paths * How frequently to send a zero-byte UDP keepalive packet
*
* There are NATs with timeouts as short as 20 seconds, so this turns out
* to be needed.
*/ */
#define ZT_PATH_HEARTBEAT_PERIOD 10000 #define ZT_NAT_KEEPALIVE_DELAY 19000
/** /**
* Paths are considered inactive if they have not received traffic in this long * Delay between scans of the topology active peer DB for peers that need ping
*
* This is also how often pings will be retried to upstream peers (relays, roots)
* constantly until something is heard.
*/ */
#define ZT_PATH_ALIVE_TIMEOUT 25000 #define ZT_PING_CHECK_INVERVAL 9500
/** /**
* Minimum time between attempts to check dead paths to see if they can be re-awakened * Delay between ordinary case pings of direct links
*/ */
#define ZT_PATH_MIN_REACTIVATE_INTERVAL 2500 #define ZT_PEER_DIRECT_PING_DELAY 60000
/**
* 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)
/**
* 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)
@@ -294,14 +270,19 @@
#define ZT_PEER_ACTIVITY_TIMEOUT 500000 #define ZT_PEER_ACTIVITY_TIMEOUT 500000
/** /**
* General rate limit timeout for multiple packet types (HELLO, etc.) * Timeout for path activity
*/ */
#define ZT_PEER_GENERAL_INBOUND_RATE_LIMIT 500 #define ZT_PATH_ACTIVITY_TIMEOUT ZT_PEER_ACTIVITY_TIMEOUT
/** /**
* General limit for max RTT for requests over the network * No answer timeout to trigger dead path detection
*/ */
#define ZT_GENERAL_RTT_LIMIT 5000 #define ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT 2000
/**
* 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
@@ -320,6 +301,21 @@
*/ */
#define ZT_MIN_UNITE_INTERVAL 30000 #define ZT_MIN_UNITE_INTERVAL 30000
/**
* Delay between initial direct NAT-t packet and more aggressive techniques
*
* 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
/**
* 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
* *
@@ -334,7 +330,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 32 #define ZT_MAX_BRIDGE_SPAM 16
/** /**
* Interval between direct path pushes in milliseconds * Interval between direct path pushes in milliseconds
@@ -361,30 +357,23 @@
#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 4 #define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 4
/** /**
* Time horizon for VERB_NETWORK_CREDENTIALS cutoff * Enable support for old Dictionary based network configs
*/
#define ZT_PEER_CREDENTIALS_CUTOFF_TIME 60000
/**
* Maximum number of VERB_NETWORK_CREDENTIALS within cutoff time
*/
#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
/**
* 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 #define ZT_SUPPORT_OLD_STYLE_NETCONF 1
/**
* A test pseudo-network-ID that can be joined
*
* 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
/** /**
* 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)
*/ */

View File

@@ -20,441 +20,261 @@
#define ZT_DICTIONARY_HPP #define ZT_DICTIONARY_HPP
#include "Constants.hpp" #include "Constants.hpp"
#include "Utils.hpp"
#include "Buffer.hpp" #ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
#include "Address.hpp"
#include <stdint.h> #include <stdint.h>
#include <string>
#include <vector>
#include <stdexcept>
#include <algorithm>
#include "Utils.hpp"
// Three fields are added/updated by sign()
#define ZT_DICTIONARY_SIGNATURE "~!ed25519"
#define ZT_DICTIONARY_SIGNATURE_IDENTITY "~!sigid"
#define ZT_DICTIONARY_SIGNATURE_TIMESTAMP "~!sigts"
namespace ZeroTier { namespace ZeroTier {
class Identity;
/** /**
* A small (in code and data) packed key=value store * Simple key/value dictionary with string serialization
* *
* This stores data in the form of a compact blob that is sort of human * The serialization format is a flat key=value with backslash escape.
* readable (depending on whether you put binary data in it) and is backward * It does not support comments or other syntactic complexities. It is
* compatible with older versions. Binary data is escaped such that the * human-readable if the keys and values in the dictionary are also
* serialized form of a Dictionary is always a valid null-terminated C string. * human-readable. Otherwise it might contain unprintable characters.
* *
* Keys are restricted: no binary data, no CR/LF, and no equals (=). If a key * Keys beginning with "~!" are reserved for signature data fields.
* contains these characters it may not be retrievable. This is not checked.
* *
* Lookup is via linear search and will be slow with a lot of keys. It's * It's stored as a simple vector and can be linearly scanned or
* designed for small things. * binary searched. Dictionaries are only used for very small things
* * outside the core loop, so this is not a significant performance
* There is code to test and fuzz this in selftest.cpp. Fuzzing a blob of * issue and it reduces memory use and code footprint.
* pointer tricks like this is important after any modifications.
*
* This is used for network configurations and for saving some things on disk
* in the ZeroTier One service code.
*
* @tparam C Dictionary max capacity in bytes
*/ */
template<unsigned int C> class Dictionary : public std::vector< std::pair<std::string,std::string> >
class Dictionary
{ {
public: public:
Dictionary() Dictionary() {}
{
_d[0] = (char)0;
}
Dictionary(const char *s)
{
Utils::scopy(_d,sizeof(_d),s);
}
Dictionary(const char *s,unsigned int len)
{
if (len > (C-1))
len = C-1;
memcpy(_d,s,len);
_d[len] = (char)0;
}
Dictionary(const Dictionary &d)
{
Utils::scopy(_d,sizeof(_d),d._d);
}
inline Dictionary &operator=(const Dictionary &d)
{
Utils::scopy(_d,sizeof(_d),d._d);
return *this;
}
/** /**
* Load a dictionary from a C-string * @param s String-serialized dictionary
* * @param maxlen Maximum length of buffer
* @param s Dictionary in string form
* @return False if 's' was longer than our capacity
*/ */
inline bool load(const char *s) Dictionary(const char *s,unsigned int maxlen) { fromString(s,maxlen); }
{
return Utils::scopy(_d,sizeof(_d),s);
}
/** /**
* Delete all entries * @param s String-serialized dictionary
*/ */
inline void clear() Dictionary(const std::string &s) { fromString(s.c_str(),(unsigned int)s.length()); }
{
_d[0] = (char)0; iterator find(const std::string &key);
} const_iterator find(const std::string &key) const;
/** /**
* @return Size of dictionary in bytes not including terminating NULL * Get a key, returning a default if not present
*/
inline unsigned int sizeBytes() const
{
for(unsigned int i=0;i<C;++i) {
if (!_d[i])
return i;
}
return C-1;
}
/**
* Get an entry
*
* Note that to get binary values, dest[] should be at least one more than
* the maximum size of the value being retrieved. That's because even if
* the data is binary a terminating 0 is still appended to dest[] after it.
*
* If the key is not found, dest[0] is set to 0 to make dest[] an empty
* C string in that case. The dest[] array will *never* be unterminated
* after this call.
*
* Security note: if 'key' is ever directly based on anything that is not
* a hard-code or internally-generated name, it must be checked to ensure
* that the buffer is NULL-terminated since key[] does not take a secondary
* size parameter. In NetworkConfig all keys are hard-coded strings so this
* isn't a problem in the core.
* *
* @param key Key to look up * @param key Key to look up
* @param dest Destination buffer * @param dfl Default if not present
* @param destlen Size of destination buffer * @return Value or default
* @return -1 if not found, or actual number of bytes stored in dest[] minus trailing 0
*/ */
inline int get(const char *key,char *dest,unsigned int destlen) const inline const std::string &get(const std::string &key,const std::string &dfl) const
{ {
const char *p = _d; const_iterator e(find(key));
const char *const eof = p + C; if (e == end())
const char *k; return dfl;
bool esc; return e->second;
int j;
if (!destlen) // sanity check
return -1;
while (*p) {
k = key;
while ((*k)&&(*p)) {
if (*p != *k)
break;
++k;
if (++p == eof) {
dest[0] = (char)0;
return -1;
}
}
if ((!*k)&&(*p == '=')) {
j = 0;
esc = false;
++p;
while ((*p != 0)&&(*p != '\r')&&(*p != '\n')) {
if (esc) {
esc = false;
switch(*p) {
case 'r': dest[j++] = '\r'; break;
case 'n': dest[j++] = '\n'; break;
case '0': dest[j++] = (char)0; break;
case 'e': dest[j++] = '='; break;
default: dest[j++] = *p; break;
}
if (j == (int)destlen) {
dest[j-1] = (char)0;
return j-1;
}
} else if (*p == '\\') {
esc = true;
} else {
dest[j++] = *p;
if (j == (int)destlen) {
dest[j-1] = (char)0;
return j-1;
}
}
if (++p == eof) {
dest[0] = (char)0;
return -1;
}
}
dest[j] = (char)0;
return j;
} else {
while ((*p)&&(*p != '\r')&&(*p != '\n')) {
if (++p == eof) {
dest[0] = (char)0;
return -1;
}
}
if (*p) {
if (++p == eof) {
dest[0] = (char)0;
return -1;
}
}
else break;
}
}
dest[0] = (char)0;
return -1;
} }
/** /**
* Get the contents of a key into a buffer
*
* @param key Key to get * @param key Key to get
* @param dest Destination buffer * @param dfl Default boolean result if key not found or empty (default: false)
* @return True if key was found (if false, dest will be empty) * @return Boolean value of key
* @tparam BC Buffer capacity (usually inferred)
*/ */
template<unsigned int BC> bool getBoolean(const std::string &key,bool dfl = false) const;
inline bool get(const char *key,Buffer<BC> &dest) const
{
const int r = this->get(key,const_cast<char *>(reinterpret_cast<const char *>(dest.data())),BC);
if (r >= 0) {
dest.setSize((unsigned int)r);
return true;
} else {
dest.clear();
return false;
}
}
/** /**
* Get a boolean value * @param key Key to get
* * @param dfl Default value if not present (default: 0)
* @param key Key to look up * @return Value converted to unsigned 64-bit int or 0 if not found
* @param dfl Default value if not found in dictionary
* @return Boolean value of key or 'dfl' if not found
*/ */
bool getB(const char *key,bool dfl = false) const inline uint64_t getUInt(const std::string &key,uint64_t dfl = 0) const
{ {
char tmp[4]; const_iterator e(find(key));
if (this->get(key,tmp,sizeof(tmp)) >= 0) if (e == end())
return ((*tmp == '1')||(*tmp == 't')||(*tmp == 'T'));
return dfl; return dfl;
return Utils::strToU64(e->second.c_str());
} }
/** /**
* Get an unsigned int64 stored as hex in the dictionary * @param key Key to get
* * @param dfl Default value if not present (default: 0)
* @param key Key to look up * @return Value converted to unsigned 64-bit int or 0 if not found
* @param dfl Default value or 0 if unspecified
* @return Decoded hex UInt value or 'dfl' if not found
*/ */
inline uint64_t getUI(const char *key,uint64_t dfl = 0) const inline uint64_t getHexUInt(const std::string &key,uint64_t dfl = 0) const
{ {
char tmp[128]; const_iterator e(find(key));
if (this->get(key,tmp,sizeof(tmp)) >= 1) if (e == end())
return Utils::hexStrToU64(tmp);
return dfl; return dfl;
return Utils::hexStrToU64(e->second.c_str());
} }
/** /**
* Add a new key=value pair * @param key Key to get
* * @param dfl Default value if not present (default: 0)
* If the key is already present this will append another, but the first * @return Value converted to signed 64-bit int or 0 if not found
* will always be returned by get(). This is not checked. If you want to
* ensure a key is not present use erase() first.
*
* Use the vlen parameter to add binary values. Nulls will be escaped.
*
* @param key Key -- nulls, CR/LF, and equals (=) are illegal characters
* @param value Value to set
* @param vlen Length of value in bytes or -1 to treat value[] as a C-string and look for terminating 0
* @return True if there was enough room to add this key=value pair
*/ */
inline bool add(const char *key,const char *value,int vlen = -1) inline int64_t getInt(const std::string &key,int64_t dfl = 0) const
{ {
for(unsigned int i=0;i<C;++i) { const_iterator e(find(key));
if (!_d[i]) { if (e == end())
unsigned int j = i; return dfl;
return Utils::strTo64(e->second.c_str());
if (j > 0) {
_d[j++] = '\n';
if (j == C) {
_d[i] = (char)0;
return false;
}
} }
const char *p = key; std::string &operator[](const std::string &key);
while (*p) {
_d[j++] = *(p++);
if (j == C) {
_d[i] = (char)0;
return false;
}
}
_d[j++] = '='; /**
if (j == C) { * @param key Key to set
_d[i] = (char)0; * @param value String value
return false; */
} inline void set(const std::string &key,const char *value)
{
p = value; (*this)[key] = value;
int k = 0;
while ( ((vlen < 0)&&(*p)) || (k < vlen) ) {
switch(*p) {
case 0:
case '\r':
case '\n':
case '\\':
case '=':
_d[j++] = '\\';
if (j == C) {
_d[i] = (char)0;
return false;
}
switch(*p) {
case 0: _d[j++] = '0'; break;
case '\r': _d[j++] = 'r'; break;
case '\n': _d[j++] = 'n'; break;
case '\\': _d[j++] = '\\'; break;
case '=': _d[j++] = 'e'; break;
}
if (j == C) {
_d[i] = (char)0;
return false;
}
break;
default:
_d[j++] = *p;
if (j == C) {
_d[i] = (char)0;
return false;
}
break;
}
++p;
++k;
}
_d[j] = (char)0;
return true;
}
}
return false;
} }
/** /**
* Add a boolean as a '1' or a '0' * @param key Key to set
* @param value String value
*/ */
inline bool add(const char *key,bool value) inline void set(const std::string &key,const std::string &value)
{ {
return this->add(key,(value) ? "1" : "0",1); (*this)[key] = value;
} }
/** /**
* Add a 64-bit integer (unsigned) as a hex value * @param key Key to set
* @param value Boolean value
*/ */
inline bool add(const char *key,uint64_t value) inline void set(const std::string &key,bool value)
{ {
char tmp[32]; (*this)[key] = ((value) ? "1" : "0");
}
/**
* @param key Key to set
* @param value Integer value
*/
inline void set(const std::string &key,uint64_t value)
{
char tmp[24];
Utils::snprintf(tmp,sizeof(tmp),"%llu",(unsigned long long)value);
(*this)[key] = tmp;
}
/**
* @param key Key to set
* @param value Integer value
*/
inline void set(const std::string &key,int64_t value)
{
char tmp[24];
Utils::snprintf(tmp,sizeof(tmp),"%lld",(long long)value);
(*this)[key] = tmp;
}
/**
* @param key Key to set
* @param value Integer value
*/
inline void setHex(const std::string &key,uint64_t value)
{
char tmp[24];
Utils::snprintf(tmp,sizeof(tmp),"%llx",(unsigned long long)value); Utils::snprintf(tmp,sizeof(tmp),"%llx",(unsigned long long)value);
return this->add(key,tmp,-1); (*this)[key] = tmp;
}
/**
* Add a 64-bit integer (unsigned) as a hex value
*/
inline bool add(const char *key,const Address &a)
{
char tmp[32];
Utils::snprintf(tmp,sizeof(tmp),"%.10llx",(unsigned long long)a.toInt());
return this->add(key,tmp,-1);
}
/**
* Add a binary buffer's contents as a value
*
* @tparam BC Buffer capacity (usually inferred)
*/
template<unsigned int BC>
inline bool add(const char *key,const Buffer<BC> &value)
{
return this->add(key,(const char *)value.data(),(int)value.size());
} }
/** /**
* @param key Key to check * @param key Key to check
* @return True if key is present * @return True if dictionary contains key
*/ */
inline bool contains(const char *key) const inline bool contains(const std::string &key) const { return (find(key) != end()); }
{
char tmp[2];
return (this->get(key,tmp,2) >= 0);
}
/** /**
* Erase a key from this dictionary * @return String-serialized dictionary
* */
* Use this before add() to ensure that a key is replaced if it might std::string toString() const;
* already be present.
/**
* Clear and initialize from a string
* *
* @param s String-serialized dictionary
* @param maxlen Maximum length of string buffer
*/
void fromString(const char *s,unsigned int maxlen);
inline void fromString(const std::string &s) { fromString(s.c_str(),(unsigned int)s.length()); }
void updateFromString(const char *s,unsigned int maxlen);
inline void update(const char *s,unsigned int maxlen) { updateFromString(s, maxlen); }
inline void update(const std::string &s) { updateFromString(s.c_str(),(unsigned int)s.length()); }
/**
* @return True if this dictionary is cryptographically signed
*/
inline bool hasSignature() const { return (find(ZT_DICTIONARY_SIGNATURE) != end()); }
/**
* @return Signing identity in string-serialized format or empty string if none
*/
inline std::string signingIdentity() const { return get(ZT_DICTIONARY_SIGNATURE_IDENTITY,std::string()); }
/**
* @return Signature timestamp in milliseconds since epoch or 0 if none
*/
uint64_t signatureTimestamp() const;
/**
* @param key Key to erase * @param key Key to erase
* @return True if key was found and erased
*/ */
inline bool erase(const char *key) void eraseKey(const std::string &key);
/**
* Remove any signature from this dictionary
*/
inline void removeSignature()
{ {
char d2[C]; eraseKey(ZT_DICTIONARY_SIGNATURE);
char *saveptr = (char *)0; eraseKey(ZT_DICTIONARY_SIGNATURE_IDENTITY);
unsigned int d2ptr = 0; eraseKey(ZT_DICTIONARY_SIGNATURE_TIMESTAMP);
bool found = false;
for(char *f=Utils::stok(_d,"\r\n",&saveptr);(f);f=Utils::stok((char *)0,"\r\n",&saveptr)) {
if (*f) {
const char *p = f;
const char *k = key;
while ((*k)&&(*p)) {
if (*k != *p)
break;
++k;
++p;
}
if (*k) {
p = f;
while (*p)
d2[d2ptr++] = *(p++);
d2[d2ptr++] = '\n';
} else {
found = true;
}
}
}
d2[d2ptr++] = (char)0;
memcpy(_d,d2,d2ptr);
return found;
} }
/** /**
* @return Value of C template parameter * Add or update signature fields with a signature of all other keys and values
*
* @param with Identity to sign with (must have secret key)
* @param now Current time
* @return True on success
*/ */
inline unsigned int capacity() const { return C; } bool sign(const Identity &id,uint64_t now);
inline const char *data() const { return _d; } /**
inline char *unsafeData() { return _d; } * Verify signature against an identity
*
* @param id Identity to verify against
* @return True if signature verification OK
*/
bool verify(const Identity &id) const;
private: private:
char _d[C]; void _mkSigBuf(std::string &buf) const;
static void _appendEsc(const char *data,unsigned int len,std::string &to);
}; };
} // namespace ZeroTier } // namespace ZeroTier
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
#endif #endif

View File

@@ -103,9 +103,9 @@ public:
friend class Hashtable::Iterator; friend class Hashtable::Iterator;
/** /**
* @param bc Initial capacity in buckets (default: 64, must be nonzero) * @param bc Initial capacity in buckets (default: 128, must be nonzero)
*/ */
Hashtable(unsigned long bc = 64) : Hashtable(unsigned long bc = 128) :
_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 (unsigned long)obj.hashCode(); return obj.hashCode();
} }
static inline unsigned long _hc(const uint64_t i) static inline unsigned long _hc(const uint64_t i)
{ {

View File

@@ -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 == ZT_OBJECT_TYPE_IDENTITY r.append(":0:"); // 0 == IDENTITY_TYPE_C25519
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(':');

View File

@@ -46,6 +46,14 @@ 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)
{ {
@@ -197,6 +205,11 @@ 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
*/ */
@@ -213,7 +226,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((uint8_t)0); // C25519/Ed25519 identity type b.append((unsigned char)IDENTITY_TYPE_C25519);
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());
@@ -244,7 +257,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++] != 0) if (b[p++] != IDENTITY_TYPE_C25519)
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());
@@ -282,24 +295,6 @@ 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

View File

@@ -22,7 +22,7 @@
#include <stdexcept> #include <stdexcept>
#include "Packet.hpp" #include "Packet.hpp"
#include "Path.hpp" #include "InetAddress.hpp"
#include "Utils.hpp" #include "Utils.hpp"
#include "MulticastGroup.hpp" #include "MulticastGroup.hpp"
#include "Peer.hpp" #include "Peer.hpp"
@@ -56,40 +56,59 @@ 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 path Path over which packet arrived * @param localAddress Local interface address
* @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 SharedPtr<Path> &path,uint64_t now) : IncomingPacket(const void *data,unsigned int len,const InetAddress &localAddress,const InetAddress &remoteAddress,uint64_t now) :
Packet(data,len), Packet(data,len),
_receiveTime(now), _receiveTime(now),
_path(path) _localAddress(localAddress),
_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 path Path over which packet arrived * @param localAddress Local interface address
* @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 SharedPtr<Path> &path,uint64_t now) inline void init(const void *data,unsigned int len,const InetAddress &localAddress,const InetAddress &remoteAddress,uint64_t now)
{ {
copyFrom(data,len); copyFrom(data,len);
_receiveTime = now; _receiveTime = now;
_path = path; _localAddress = localAddress;
_remoteAddress = remoteAddress;
} }
/** /**
@@ -99,12 +118,21 @@ 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. * may no longer be valid. The only exception is deferred decoding. In this
* 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 tryDecode(const RuntimeEnvironment *RR,bool deferred);
/** /**
* @return Time of packet receipt / start of decode * @return Time of packet receipt / start of decode
@@ -136,7 +164,7 @@ 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,const bool alreadyAuthenticated); bool _doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer); // can be called with NULL peer, while all others cannot
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);
@@ -144,9 +172,9 @@ 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_CREDENTIALS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); bool _doNETWORK_MEMBERSHIP_CERTIFICATE(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(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); bool _doNETWORK_CONFIG_REFRESH(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);
@@ -154,10 +182,12 @@ private:
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 _doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,const uint64_t nwid); // Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to communicate
void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid);
uint64_t _receiveTime; uint64_t _receiveTime;
SharedPtr<Path> _path; InetAddress _localAddress;
InetAddress _remoteAddress;
}; };
} // namespace ZeroTier } // namespace ZeroTier

View File

@@ -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 if (ip.find('.') != std::string::npos) { } else {
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);
@@ -279,8 +279,6 @@ bool InetAddress::containsAddress(const InetAddress &addr) const
switch(ss_family) { switch(ss_family) {
case AF_INET: { case AF_INET: {
const unsigned int bits = netmaskBits(); const unsigned int bits = netmaskBits();
if (bits == 0)
return true;
return ( (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_addr.s_addr) >> (32 - bits)) == (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr) >> (32 - bits)) ); return ( (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_addr.s_addr) >> (32 - bits)) == (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr) >> (32 - bits)) );
} }
case AF_INET6: { case AF_INET6: {
@@ -395,6 +393,7 @@ bool InetAddress::operator<(const InetAddress &a) const
} }
InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac) InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac)
throw()
{ {
struct sockaddr_in6 sin6; struct sockaddr_in6 sin6;
sin6.sin6_family = AF_INET6; sin6.sin6_family = AF_INET6;
@@ -419,6 +418,7 @@ InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac)
} }
InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress) InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
throw()
{ {
InetAddress r; InetAddress r;
struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r); struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
@@ -443,25 +443,4 @@ InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
return r; return r;
} }
InetAddress InetAddress::makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress)
{
nwid ^= (nwid >> 32);
InetAddress r;
struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
sin6->sin6_family = AF_INET6;
sin6->sin6_addr.s6_addr[0] = 0xfc;
sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 24);
sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 16);
sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 8);
sin6->sin6_addr.s6_addr[4] = (uint8_t)nwid;
sin6->sin6_addr.s6_addr[5] = (uint8_t)(zeroTierAddress >> 32);
sin6->sin6_addr.s6_addr[6] = (uint8_t)(zeroTierAddress >> 24);
sin6->sin6_addr.s6_addr[7] = (uint8_t)(zeroTierAddress >> 16);
sin6->sin6_addr.s6_addr[8] = (uint8_t)(zeroTierAddress >> 8);
sin6->sin6_addr.s6_addr[9] = (uint8_t)zeroTierAddress;
sin6->sin6_addr.s6_addr[15] = 0x01;
sin6->sin6_port = Utils::hton((uint16_t)40);
return r;
}
} // namespace ZeroTier } // namespace ZeroTier

View File

@@ -231,6 +231,7 @@ struct InetAddress : public sockaddr_storage
* @param port Port, 0 to 65535 * @param port Port, 0 to 65535
*/ */
inline void setPort(unsigned int port) inline void setPort(unsigned int port)
throw()
{ {
switch(ss_family) { switch(ss_family) {
case AF_INET: case AF_INET:
@@ -242,25 +243,6 @@ struct InetAddress : public sockaddr_storage
} }
} }
/**
* @return True if this network/netmask route describes a default route (e.g. 0.0.0.0/0)
*/
inline bool isDefaultRoute() const
{
switch(ss_family) {
case AF_INET:
return ( (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == 0) && (reinterpret_cast<const struct sockaddr_in *>(this)->sin_port == 0) );
case AF_INET6:
const uint8_t *ipb = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
for(int i=0;i<16;++i) {
if (ipb[i])
return false;
}
return (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port == 0);
}
return false;
}
/** /**
* @return ASCII IP/port format representation * @return ASCII IP/port format representation
*/ */
@@ -300,19 +282,6 @@ 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()
* *
@@ -369,6 +338,7 @@ 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);
@@ -377,25 +347,6 @@ 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()
* *
@@ -414,25 +365,6 @@ 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<sizeof(InetAddress);++i)
reinterpret_cast<uint8_t *>(&tmp)[i % sizeof(tmp)] ^= a[i];
return tmp;
}
}
/** /**
* Set to null/zero * Set to null/zero
*/ */
@@ -521,7 +453,8 @@ struct InetAddress : public sockaddr_storage
* @param mac MAC address seed * @param mac MAC address seed
* @return IPv6 link-local address * @return IPv6 link-local address
*/ */
static InetAddress makeIpv6LinkLocal(const MAC &mac); static InetAddress makeIpv6LinkLocal(const MAC &mac)
throw();
/** /**
* Compute private IPv6 unicast address from network ID and ZeroTier address * Compute private IPv6 unicast address from network ID and ZeroTier address
@@ -564,12 +497,8 @@ struct InetAddress : public sockaddr_storage
* @param zeroTierAddress 40-bit device address (in least significant 40 bits, highest 24 bits ignored) * @param zeroTierAddress 40-bit device address (in least significant 40 bits, highest 24 bits ignored)
* @return IPv6 private unicast address with /88 netmask * @return IPv6 private unicast address with /88 netmask
*/ */
static InetAddress makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress); static InetAddress makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
throw();
/**
* Compute a private IPv6 "6plane" unicast address from network ID and ZeroTier address
*/
static InetAddress makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress);
}; };
} // namespace ZeroTier } // namespace ZeroTier

View File

@@ -60,6 +60,16 @@ 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
* *
@@ -96,6 +106,22 @@ 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
*/ */

View File

@@ -34,8 +34,8 @@ namespace ZeroTier {
Multicaster::Multicaster(const RuntimeEnvironment *renv) : Multicaster::Multicaster(const RuntimeEnvironment *renv) :
RR(renv), RR(renv),
_groups(256), _groups(1024),
_gatherAuth(256) _groups_m()
{ {
} }
@@ -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,
disableCompression, com,
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,38 +226,33 @@ 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();
explicitGatherPeers[1] = RR->topology->getPeer(Network::controllerFor(nwid));
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());
Address explicitGatherPeers[16]; const CertificateOfMembership *com = (CertificateOfMembership *)0;
unsigned int numExplicitGatherPeers = 0; {
SharedPtr<Peer> bestRoot(RR->topology->getBestRoot()); SharedPtr<Network> nw(RR->node->network(nwid));
if (bestRoot) if ((nw)&&(nw->hasConfig())&&(nw->config().com)&&(nw->config().isPrivate())&&(p->needsOurNetworkMembershipCertificate(nwid,now,true)))
explicitGatherPeers[numExplicitGatherPeers++] = bestRoot->address(); com = &(nw->config().com);
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;
}
}
} }
for(unsigned int k=0;k<numExplicitGatherPeers;++k) { Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
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->node->expectReplyTo(outp.packetId()); RR->sw->send(outp,true,0);
RR->sw->send(outp,true);
} }
gatherLimit = 0;
} }
gs.txQueue.push_back(OutboundMulticast()); gs.txQueue.push_back(OutboundMulticast());
@@ -267,7 +262,7 @@ void Multicaster::send(
RR, RR,
now, now,
nwid, nwid,
disableCompression, com,
limit, limit,
gatherLimit, gatherLimit,
src, src,
@@ -303,9 +298,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);
@@ -340,26 +335,6 @@ void Multicaster::clean(uint64_t now)
} }
} }
{
Mutex::Lock _l(_gatherAuth_m);
_GatherAuthKey *k = (_GatherAuthKey *)0;
uint64_t *ts = (uint64_t *)ts;
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

View File

@@ -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,52 +181,12 @@ 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 (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

View File

@@ -40,17 +40,14 @@
#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
@@ -58,6 +55,7 @@ class Peer;
class Network : NonCopyable class Network : NonCopyable
{ {
friend class SharedPtr<Network>; friend class SharedPtr<Network>;
friend class _MulticastAnnounceAll; // internal function object
public: public:
/** /**
@@ -65,11 +63,6 @@ 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
* *
@@ -84,78 +77,43 @@ public:
~Network(); ~Network();
inline uint64_t id() const { return _id; }
inline Address controller() const { return Address(_id >> 24); }
inline bool multicastEnabled() const { return (_config.multicastLimit > 0); }
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; }
/** /**
* Apply filters to an outgoing packet * @return Network ID
*
* 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
*/ */
bool filterOutgoingPacket( inline uint64_t id() const throw() { return _id; }
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);
/** /**
* Apply filters to an incoming packet * @return Address of network's controller (most significant 40 bits of ID)
*
* 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
*/ */
int filterIncomingPacket( inline Address controller() const throw() { return Address(_id >> 24); }
const SharedPtr<Peer> &sourcePeer,
const Address &ztDest, /**
const MAC &macSource, * @param nwid Network ID
const MAC &macDest, * @return Address of network's controller
const uint8_t *frameData, */
const unsigned int frameLen, static inline Address controllerFor(uint64_t nwid) throw() { return Address(nwid >> 24); }
const unsigned int etherType,
const unsigned int vlanId); /**
* @return Multicast group memberships for this network's port (local, not learned via bridging)
*/
inline std::vector<MulticastGroup> multicastGroups() const
{
Mutex::Lock _l(_lock);
return _myMulticastGroups;
}
/**
* @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 check groups we've learned via bridging * @param includeBridgedGroups If true, also include any 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;
@@ -175,17 +133,30 @@ public:
void multicastUnsubscribe(const MulticastGroup &mg); void multicastUnsubscribe(const MulticastGroup &mg);
/** /**
* Handle an inbound network config chunk * Announce multicast groups to a peer if that peer is authorized on this network
* *
* This is called from IncomingPacket to handle incoming network config * @param peer Peer to try to announce multicast groups to
* chunks via OK(NETWORK_CONFIG_REQUEST) or NETWORK_CONFIG. It verifies * @return True if peer was authorized and groups were announced
* each chunk and once assembled applies the configuration.
*
* @param chunk Packet 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
*/ */
uint64_t handleConfigChunk(const Packet &chunk,unsigned int ptr); bool tryAnnounceMulticastGroupsTo(const SharedPtr<Peer> &peer);
/**
* Apply a NetworkConfig to this network
*
* @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 confBytes Network configuration in old-style Dictionary or new-style serialized format
* @param confLen Length of network configuration in bytes
* @param saveToDisk IF true (default), write config to disk
* @return 0 -- rejected, 1 -- accepted but not new, 2 -- accepted new config
*/
int setConfiguration(const void *confBytes,unsigned int confLen,bool saveToDisk);
/** /**
* Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this * Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
@@ -197,7 +168,7 @@ public:
} }
/** /**
* Set netconf failure to 'not found' -- called by IncomingPacket when controller reports this * Set netconf failure to 'not found' -- called by PacketDecider when controller reports this
*/ */
inline void setNotFound() inline void setNotFound()
{ {
@@ -211,24 +182,73 @@ public:
void requestConfiguration(); void requestConfiguration();
/** /**
* Determine whether this peer is permitted to communicate on this network * @param peer Peer to check
* @return True if peer is allowed to communicate on this network
*/ */
bool gate(const SharedPtr<Peer> &peer); inline bool isAllowed(const SharedPtr<Peer> &peer) const
{
Mutex::Lock _l(_lock);
return _isAllowed(peer);
}
/** /**
* Do periodic cleanup and housekeeping tasks * Perform cleanup and possibly save state
*/ */
void clean(); void clean();
/** /**
* Push state to members such as multicast group memberships and latest COM (if needed) * @return Time of last updated configuration or 0 if none
*/ */
inline void sendUpdatesToMembers() inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
/**
* @return Status of this network
*/
inline ZT_VirtualNetworkStatus status() const
{ {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
_sendUpdatesToMembers((const MulticastGroup *)0); return _status();
} }
/**
* @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)
* *
@@ -239,7 +259,9 @@ public:
{ {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
const Address *const br = _remoteBridgeRoutes.get(mac); const Address *const br = _remoteBridgeRoutes.get(mac);
return ((br) ? *br : Address()); if (br)
return *br;
return Address();
} }
/** /**
@@ -259,48 +281,14 @@ 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 * @return True if traffic on this network's tap is enabled
*/ */
Membership::AddCredentialResult addCredential(const CertificateOfMembership &com); inline bool enabled() const throw() { return _enabled; }
/** /**
* Validate a credential and learn it if it passes certificate and other checks * @param enabled Should traffic be allowed on this network?
*/ */
inline Membership::AddCredentialResult addCredential(const Capability &cap) void setEnabled(bool enabled);
{
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);
/**
* 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
@@ -312,57 +300,40 @@ public:
void destroy(); void destroy();
/** /**
* Get this network's config for export via the ZT core API * @return Pointer to user PTR (modifiable user ptr used in 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:
int _setConfiguration(const NetworkConfig &nconf,bool saveToDisk);
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 _gate(const SharedPtr<Peer> &peer); bool _isAllowed(const SharedPtr<Peer> &peer) const;
void _sendUpdatesToMembers(const MulticastGroup *const newMulticastGroup); void _announceMulticastGroups();
void _announceMulticastGroupsTo(const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups); void _announceMulticastGroupsTo(const SharedPtr<Peer> &peer,const std::vector<MulticastGroup> &allMulticastGroups) const;
std::vector<MulticastGroup> _allMulticastGroups() const; std::vector<MulticastGroup> _allMulticastGroups() const;
Membership &_membership(const Address &a);
const RuntimeEnvironment *const RR; const RuntimeEnvironment *RR;
void *_uPtr; void *_uPtr;
const uint64_t _id; uint64_t _id;
uint64_t _lastAnnouncedMulticastGroupsUpstream;
MAC _mac; // local MAC address MAC _mac; // local MAC address
bool _portInitialized; volatile bool _enabled;
volatile 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;
uint64_t _lastConfigUpdate; volatile uint64_t _lastConfigUpdate;
struct _IncomingConfigChunk volatile bool _destroyed;
{
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,
@@ -370,9 +341,7 @@ private:
NETCONF_FAILURE_NOT_FOUND, NETCONF_FAILURE_NOT_FOUND,
NETCONF_FAILURE_INIT_FAILED NETCONF_FAILURE_INIT_FAILED
} _netconfFailure; } _netconfFailure;
int _portError; // return value from port config callback volatile int _portError; // return value from port config callback
Hashtable<Address,Membership> _memberships;
Mutex _lock; Mutex _lock;

View File

@@ -18,328 +18,163 @@
#include <stdint.h> #include <stdint.h>
#include <algorithm>
#include "NetworkConfig.hpp" #include "NetworkConfig.hpp"
#include "Utils.hpp" #include "Utils.hpp"
namespace ZeroTier { namespace ZeroTier {
bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const
{
Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
try {
d.clear();
// Try to put the more human-readable fields first
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_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_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_MULTICAST_LIMIT,(uint64_t)this->multicastLimit)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)this->type)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name)) return false;
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF #ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
if (includeLegacy) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD,this->allowPassiveBridging())) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD,this->enableBroadcast())) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,this->isPrivate())) return false;
std::string v4s; void NetworkConfig::fromDictionary(const char *ds,unsigned int dslen)
for(unsigned int i=0;i<staticIpCount;++i) {
if (this->staticIps[i].ss_family == AF_INET) {
if (v4s.length() > 0)
v4s.push_back(',');
v4s.append(this->staticIps[i].toString());
}
}
if (v4s.length() > 0) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD,v4s.c_str())) return false;
}
std::string v6s;
for(unsigned int i=0;i<staticIpCount;++i) {
if (this->staticIps[i].ss_family == AF_INET6) {
if (v6s.length() > 0)
v6s.push_back(',');
v6s.append(this->staticIps[i].toString());
}
}
if (v6s.length() > 0) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD,v6s.c_str())) return false;
}
std::string ets;
unsigned int et = 0;
ZT_VirtualNetworkRuleType lastrt = ZT_NETWORK_RULE_ACTION_ACCEPT;
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 (((int)lastrt < 32)||(lastrt == ZT_NETWORK_RULE_MATCH_ETHERTYPE)) {
if (ets.length() > 0)
ets.push_back(',');
char tmp2[16];
Utils::snprintf(tmp2,sizeof(tmp2),"%x",et);
ets.append(tmp2);
}
et = 0;
}
lastrt = rt;
}
if (ets.length() > 0) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD,ets.c_str())) return false;
}
if (this->com) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD,this->com.toString().c_str())) return false;
}
std::string ab;
for(unsigned int i=0;i<this->specialistCount;++i) {
if ((this->specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) {
if (ab.length() > 0)
ab.push_back(',');
ab.append(Address(this->specialists[i]).toString().c_str());
}
}
if (ab.length() > 0) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,ab.c_str())) return false;
}
}
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
// Then add binary blobs
if (this->com) {
tmp->clear();
this->com.serialize(*tmp);
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp)) return false;
}
tmp->clear();
for(unsigned int i=0;i<this->capabilityCount;++i)
this->capabilities[i].serialize(*tmp);
if (tmp->size()) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,*tmp)) return false;
}
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->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) {
reinterpret_cast<const InetAddress *>(&(this->routes[i].target))->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].metric);
}
if (tmp->size()) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) return false;
}
tmp->clear();
for(unsigned int i=0;i<this->staticIpCount;++i)
this->staticIps[i].serialize(*tmp);
if (tmp->size()) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) return false;
}
if (this->ruleCount) {
tmp->clear();
Capability::serializeRules(*tmp,rules,ruleCount);
if (tmp->size()) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) return false;
}
}
delete tmp;
} catch ( ... ) {
delete tmp;
throw;
}
return true;
}
bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d)
{ {
Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>(); static const std::string zero("0");
static const std::string one("1");
Dictionary d(ds,dslen);
try {
memset(this,0,sizeof(NetworkConfig)); memset(this,0,sizeof(NetworkConfig));
// Fields that are always present, new or old // NOTE: d.get(name) throws if not found, d.get(name,default) returns default
this->networkId = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,0);
if (!this->networkId) {
delete tmp;
return false;
}
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->issuedTo = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,0);
if (!this->issuedTo) {
delete tmp;
return false;
}
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));
if (d.getUI(ZT_NETWORKCONFIG_DICT_KEY_VERSION,0) < 6) { networkId = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,"0").c_str());
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF if (!networkId)
char tmp2[1024]; throw std::invalid_argument("configuration contains zero network ID");
// Decode legacy fields if version is old timestamp = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,"0").c_str());
if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD)) revision = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_REVISION,"1").c_str()); // older controllers don't send this, so default to 1
this->flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING; issuedTo = Address(d.get(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,"0"));
if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD))
this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; // always enable for old-style netconf
this->type = (d.getB(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,true)) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD,tmp2,sizeof(tmp2)) > 0) { multicastLimit = Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,zero).c_str());
char *saveptr = (char *)0; if (multicastLimit == 0) multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT;
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
if (this->staticIpCount >= ZT_MAX_ZT_ASSIGNED_ADDRESSES) break; flags |= ((Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING,zero).c_str()) != 0) ? ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING : 0);
InetAddress ip(f); flags |= ((Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST,one).c_str()) != 0) ? ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST : 0);
if (!ip.isNetwork())
this->staticIps[this->staticIpCount++] = ip; this->type = (Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE,one).c_str()) != 0) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
std::string nametmp(d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,""));
for(unsigned long i=0;((i<ZT_MAX_NETWORK_SHORT_NAME_LENGTH)&&(i<nametmp.length()));++i)
name[i] = (char)nametmp[i];
// we zeroed the entire structure above and _name is ZT_MAX_NETWORK_SHORT_NAME_LENGTH+1, so it will always null-terminate
std::vector<std::string> activeBridgesSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES,"").c_str(),",","",""));
for(std::vector<std::string>::const_iterator a(activeBridgesSplit.begin());a!=activeBridgesSplit.end();++a) {
if (a->length() == ZT_ADDRESS_LENGTH_HEX) { // ignore empty or garbage fields
Address tmp(*a);
if (!tmp.isReserved()) {
uint64_t specialist = tmp.toInt();
for(unsigned int i=0;i<specialistCount;++i) {
if ((specialists[i] & 0xffffffffffULL) == specialist) {
specialists[i] |= ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE;
specialist = 0;
break;
} }
} }
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD,tmp2,sizeof(tmp2)) > 0) { if ((specialist)&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS))
char *saveptr = (char *)0; specialists[specialistCount++] = specialist | ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE;
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) { }
if (this->staticIpCount >= ZT_MAX_ZT_ASSIGNED_ADDRESSES) break;
InetAddress ip(f);
if (!ip.isNetwork())
this->staticIps[this->staticIpCount++] = ip;
} }
} }
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD,tmp2,sizeof(tmp2)) > 0) { std::string ipAddrs(d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC,std::string()));
this->com.fromString(tmp2); {
std::string v6s(d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC,std::string()));
if (v6s.length()) {
if (ipAddrs.length())
ipAddrs.push_back(',');
ipAddrs.append(v6s);
}
}
std::vector<std::string> ipAddrsSplit(Utils::split(ipAddrs.c_str(),",","",""));
for(std::vector<std::string>::const_iterator ipstr(ipAddrsSplit.begin());ipstr!=ipAddrsSplit.end();++ipstr) {
InetAddress addr(*ipstr);
switch(addr.ss_family) {
case AF_INET:
if ((!addr.netmaskBits())||(addr.netmaskBits() > 32))
continue;
break;
case AF_INET6:
if ((!addr.netmaskBits())||(addr.netmaskBits() > 128))
continue;
break;
default: // ignore unrecognized address types or junk/empty fields
continue;
}
if (!addr.isNetwork()) {
if ((staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)&&(std::find(&(staticIps[0]),&(staticIps[staticIpCount]),addr) == &(staticIps[staticIpCount])))
staticIps[staticIpCount++] = addr;
}
}
std::sort(&(staticIps[0]),&(staticIps[staticIpCount]));
/* Old versions don't support gateways anyway, so ignore this in old netconfs
std::vector<std::string> gatewaysSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS,"").c_str(),",","",""));
for(std::vector<std::string>::const_iterator gwstr(gatewaysSplit.begin());gwstr!=gatewaysSplit.end();++gwstr) {
InetAddress gw(*gwstr);
if ((gw)&&(_gatewayCount < ZT_MAX_NETWORK_GATEWAYS)&&(std::find(&(_gateways[0]),&(_gateways[_gatewayCount]),gw) == &(_gateways[_gatewayCount])))
_gateways[_gatewayCount++] = gw;
}
std::sort(&(_gateways[0]),&(_gateways[_gatewayCount]));
*/
std::vector<std::string> relaysSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_RELAYS,"").c_str(),",","",""));
for(std::vector<std::string>::const_iterator r(relaysSplit.begin());r!=relaysSplit.end();++r) {
if (r->length() >= ZT_ADDRESS_LENGTH_HEX) {
Address zt(r->substr(0,ZT_ADDRESS_LENGTH_HEX).c_str());
InetAddress phy[2];
unsigned int phyCount = 0;
const std::size_t semi(r->find(';'));
if ((semi > ZT_ADDRESS_LENGTH_HEX)&&(semi < (r->length() - 2))) {
std::vector<std::string> phySplit(Utils::split(r->substr(semi+1).c_str(),",","",""));
for(std::vector<std::string>::const_iterator p(phySplit.begin());((p!=phySplit.end())&&(phyCount < 2));++p) {
phy[phyCount] = InetAddress(*p);
if (phy[phyCount])
++phyCount;
else phy[phyCount].zero();
}
} }
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD,tmp2,sizeof(tmp2)) > 0) { uint64_t specialist = zt.toInt();
char *saveptr = (char *)0; for(unsigned int i=0;i<specialistCount;++i) {
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) { if ((specialists[i] & 0xffffffffffULL) == specialist) {
unsigned int et = Utils::hexStrToUInt(f) & 0xffff; specialists[i] |= ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY;
if ((this->ruleCount + 2) > ZT_MAX_NETWORK_RULES) break; specialist = 0;
if (et > 0) { break;
this->rules[this->ruleCount].t = (uint8_t)ZT_NETWORK_RULE_MATCH_ETHERTYPE;
this->rules[this->ruleCount].v.etherType = (uint16_t)et;
++this->ruleCount;
} }
this->rules[this->ruleCount++].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
}
} else {
this->rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
this->ruleCount = 1;
} }
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,tmp2,sizeof(tmp2)) > 0) { if ((specialist)&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS))
char *saveptr = (char *)0; specialists[specialistCount++] = specialist | ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY;
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
this->addSpecialist(Address(f),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE); if ((phy[0])&&(pinnedCount < ZT_MAX_NETWORK_PINNED)) {
pinned[pinnedCount].zt = zt;
pinned[pinnedCount].phy = phy[0];
++pinnedCount;
}
if ((phy[1])&&(pinnedCount < ZT_MAX_NETWORK_PINNED)) {
pinned[pinnedCount].zt = zt;
pinned[pinnedCount].phy = phy[0];
++pinnedCount;
} }
} }
#else }
delete tmp;
return false; std::vector<std::string> ets(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES,"").c_str(),",","",""));
for(std::vector<std::string>::const_iterator et(ets.begin());et!=ets.end();++et) {
unsigned int et2 = Utils::hexStrToUInt(et->c_str()) & 0xffff;
if ((ruleCount + 1) < ZT_MAX_NETWORK_RULES) {
if (et2) {
rules[ruleCount].t = ZT_NETWORK_RULE_MATCH_ETHERTYPE;
rules[ruleCount].v.etherType = (uint16_t)et2;
++ruleCount;
}
rules[ruleCount++].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
}
}
this->com.fromString(d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP,std::string()));
}
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF #endif // ZT_SUPPORT_OLD_STYLE_NETCONF
} else {
// Otherwise we can use the new fields
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);
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp))
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_TAGS,*tmp)) {
try {
unsigned int p = 0;
while (p < tmp->size()) {
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_SPECIALISTS,*tmp)) {
unsigned int p = 0;
while (((p + 8) <= tmp->size())&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS)) {
this->specialists[this->specialistCount++] = tmp->at<uint64_t>(p);
p += 8;
}
}
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) {
unsigned int p = 0;
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].via))->deserialize(*tmp,p);
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->routeCount;
}
}
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) {
unsigned int p = 0;
while ((p < tmp->size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
p += this->staticIps[this->staticIpCount++].deserialize(*tmp,p);
}
}
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) {
this->ruleCount = 0;
unsigned int p = 0;
Capability::deserializeRules(*tmp,p,this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES);
}
}
//printf("~~~\n%s\n~~~\n",d.data());
//dump();
//printf("~~~\n");
delete tmp;
return true;
} catch ( ... ) {
delete tmp;
return false;
}
}
} // namespace ZeroTier } // namespace ZeroTier

View File

@@ -35,51 +35,26 @@
#include "MulticastGroup.hpp" #include "MulticastGroup.hpp"
#include "Address.hpp" #include "Address.hpp"
#include "CertificateOfMembership.hpp" #include "CertificateOfMembership.hpp"
#include "Capability.hpp"
#include "Tag.hpp" #ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
#include "Dictionary.hpp" #include "Dictionary.hpp"
#include "Identity.hpp" #include <string>
#endif
/**
* 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)
*/ */
#define ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING 0x0000000000000001ULL #define ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING 0x0001
/** /**
* Flag: enable broadcast * Flag: enable broadcast
*/ */
#define ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST 0x0000000000000002ULL #define ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST 0x0002
/** /**
* Flag: enable IPv6 NDP emulation for certain V6 address patterns * Device is a network preferred relay
*/ */
#define ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION 0x0000000000000004ULL #define ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY 0x0000010000000000ULL
/**
* Flag: result of unrecognized MATCH entries in a rules table: match if set, no-match if clear
*/
#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
@@ -87,62 +62,24 @@
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE 0x0000020000000000ULL #define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE 0x0000020000000000ULL
/** /**
* Anchors are stable devices on this network that can cache multicast info, etc. * An anchor is a device that is willing to be one and has been online/stable for a long time on this network
*/ */
#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 {
// Dictionary capacity needed for max size network config #ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
#define ZT_NETWORKCONFIG_DICT_CAPACITY (4096 + (sizeof(ZT_VirtualNetworkRule) * ZT_MAX_NETWORK_RULES) + (sizeof(Capability) * ZT_MAX_NETWORK_CAPABILITIES) + (sizeof(Tag) * ZT_MAX_NETWORK_TAGS))
// Dictionary capacity needed for max size network meta-data
#define ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY 1024
// Network config version
#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"
// Protocol version (see Packet.hpp)
#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"
// Relay policy for this node
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_RELAY_POLICY "rp"
// 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 in
// By convention we use upper case for binary blobs, but it doesn't really matter. // netconf response packets.
// network config version // integer(hex)[,integer(hex),...]
#define ZT_NETWORKCONFIG_DICT_KEY_VERSION "v" #define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES "et"
// network ID // network ID
#define ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID "nwid" #define ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID "nwid"
// integer(hex) // integer(hex)
@@ -151,57 +88,34 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_REVISION "r" #define ZT_NETWORKCONFIG_DICT_KEY_REVISION "r"
// address of member // address of member
#define ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO "id" #define ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO "id"
// flags(hex)
#define ZT_NETWORKCONFIG_DICT_KEY_FLAGS "f"
// integer(hex) // integer(hex)
#define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT "ml" #define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT "ml"
// network type (hex) // 0/1
#define ZT_NETWORKCONFIG_DICT_KEY_TYPE "t" #define ZT_NETWORKCONFIG_DICT_KEY_PRIVATE "p"
// text // text
#define ZT_NETWORKCONFIG_DICT_KEY_NAME "n" #define ZT_NETWORKCONFIG_DICT_KEY_NAME "n"
// credential time max delta in ms // text
#define ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA "ctmd" #define ZT_NETWORKCONFIG_DICT_KEY_DESC "d"
// binary serialized certificate of membership
#define ZT_NETWORKCONFIG_DICT_KEY_COM "C"
// specialists (binary array of uint64_t)
#define ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS "S"
// routes (binary blob)
#define ZT_NETWORKCONFIG_DICT_KEY_ROUTES "RT"
// static IPs (binary blob)
#define ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS "I"
// rules (binary blob)
#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"
// curve25519 signature
#define ZT_NETWORKCONFIG_DICT_KEY_SIGNATURE "C25519"
// Legacy fields -- these are obsoleted but are included when older clients query
// boolean (now a flag)
#define ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD "pb"
// boolean (now a flag)
#define ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD "eb"
// IP/bits[,IP/bits,...] // IP/bits[,IP/bits,...]
// Note that IPs that end in all zeroes are routes with no assignment in them. // Note that IPs that end in all zeroes are routes with no assignment in them.
#define ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD "v4s" #define ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC "v4s"
// IP/bits[,IP/bits,...] // IP/bits[,IP/bits,...]
// Note that IPs that end in all zeroes are routes with no assignment in them. // Note that IPs that end in all zeroes are routes with no assignment in them.
#define ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD "v6s" #define ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC "v6s"
// serialized CertificateOfMembership
#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP "com"
// 0/1 // 0/1
#define ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD "p" #define ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST "eb"
// integer(hex)[,integer(hex),...] // 0/1
#define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD "et" #define ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING "pb"
// string-serialized CertificateOfMembership
#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD "com"
// node[,node,...] // node[,node,...]
#define ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD "ab" #define ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES "ab"
// 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 "rl"
// IP/metric[,IP/metric,...]
#define ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS "gw"
// End legacy fields #endif // ZT_SUPPORT_OLD_STYLE_NETCONF
/** /**
* Network configuration received from network controller nodes * Network configuration received from network controller nodes
@@ -212,6 +126,58 @@ 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));
@@ -229,21 +195,24 @@ public:
} }
/** /**
* Write this network config to a dictionary for transport * @param etherType Ethernet frame type to check
* * @return True if allowed on this network
* @param d Dictionary
* @param includeLegacy If true, include legacy fields for old node versions
* @return True if dictionary was successfully created, false if e.g. overflow
*/ */
bool toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const; inline bool permitsEtherType(unsigned int etherType) const
{
/** unsigned int et = 0;
* Read this network config from a dictionary for(unsigned int i=0;i<ruleCount;++i) {
* ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f);
* @param d Dictionary (non-const since it might be modified during parse, should not be used after call) if (rt == ZT_NETWORK_RULE_MATCH_ETHERTYPE) {
* @return True if dictionary was valid and network config successfully initialized et = rules[i].v.etherType;
*/ } else if (rt == ZT_NETWORK_RULE_ACTION_ACCEPT) {
bool fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d); if ((!et)||(et == etherType))
return true;
et = 0;
}
}
return false;
}
/** /**
* @return True if passive bridging is allowed (experimental) * @return True if passive bridging is allowed (experimental)
@@ -255,16 +224,6 @@ public:
*/ */
inline bool enableBroadcast() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); } inline bool enableBroadcast() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); }
/**
* @return True if IPv6 NDP emulation should be allowed for certain "magic" IPv6 address patterns
*/
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)
*/ */
@@ -302,16 +261,40 @@ public:
} }
/** /**
* @param a Address to check * Get pinned physical address for a given ZeroTier address, if any
* @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 bool isAnchor(const Address &a) const inline InetAddress findPinnedAddress(const Address &zt,unsigned int af) const
{ {
for(unsigned int i=0;i<specialistCount;++i) { for(unsigned int i=0;i<pinnedCount;++i) {
if ((a == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR) != 0)) if (pinned[i].zt == zt) {
return true; if ((af == 0)||((unsigned int)pinned[i].phy.ss_family == af))
return pinned[i].phy;
} }
return false; }
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) {
if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) {
r.push_back(Relay());
r.back().address = specialists[i];
r.back().phy4 = findPinnedAddress(r.back().address,AF_INET);
r.back().phy6 = findPinnedAddress(r.back().address,AF_INET6);
}
}
return r;
} }
/** /**
@@ -330,15 +313,30 @@ public:
} }
/** /**
* @param byPeer Address to check * Iterate through relays efficiently
* @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
*/ */
inline bool circuitTestingAllowed(const Address &byPeer) const Address nextRelay(unsigned int &ptr) const
{
while (ptr < specialistCount) {
if ((specialists[ptr] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) {
return Address(specialists[ptr]);
}
++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 ((byPeer == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_CIRCUIT_TESTER) != 0)) if ((zt == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0))
return true; return true;
} }
return false; return false;
@@ -352,56 +350,270 @@ 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)); }
/** template<unsigned int C>
* Add a specialist or mask flags if already present inline void serialize(Buffer<C> &b) const
*
* This masks the existing flags if the specialist is already here or adds
* it otherwise.
*
* @param a Address of specialist
* @param f Flags (OR of specialist role/type flags)
* @return True if successfully masked or added
*/
inline bool addSpecialist(const Address &a,const uint64_t f)
{ {
const uint64_t aint = a.toInt(); b.append((uint16_t)1); // version
b.append((uint64_t)networkId);
b.append((uint64_t)timestamp);
b.append((uint64_t)revision);
issuedTo.appendTo(b);
b.append((uint32_t)multicastLimit);
b.append((uint32_t)flags);
b.append((uint8_t)type);
unsigned int nl = (unsigned int)strlen(name);
if (nl > 255) nl = 255; // sanity check
b.append((uint8_t)nl);
b.append((const void *)name,nl);
b.append((uint16_t)specialistCount);
for(unsigned int i=0;i<specialistCount;++i)
b.append((uint64_t)specialists[i]);
b.append((uint16_t)routeCount);
for(unsigned int i=0;i<routeCount;++i) {
reinterpret_cast<const InetAddress *>(&(routes[i].target))->serialize(b);
reinterpret_cast<const InetAddress *>(&(routes[i].via))->serialize(b);
}
b.append((uint16_t)staticIpCount);
for(unsigned int i=0;i<staticIpCount;++i)
staticIps[i].serialize(b);
b.append((uint16_t)pinnedCount);
for(unsigned int i=0;i<pinnedCount;++i) {
pinned[i].zt.appendTo(b);
pinned[i].phy.serialize(b);
}
b.append((uint16_t)ruleCount);
for(unsigned int i=0;i<ruleCount;++i) {
b.append((uint8_t)rules[i].t);
switch((ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f)) {
//case ZT_NETWORK_RULE_ACTION_DROP:
//case ZT_NETWORK_RULE_ACTION_ACCEPT:
default:
b.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:
b.append((uint8_t)5);
Address(rules[i].v.zt).appendTo(b);
break;
case ZT_NETWORK_RULE_MATCH_VLAN_ID:
b.append((uint8_t)2);
b.append((uint16_t)rules[i].v.vlanId);
break;
case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
b.append((uint8_t)1);
b.append((uint8_t)rules[i].v.vlanPcp);
break;
case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
b.append((uint8_t)1);
b.append((uint8_t)rules[i].v.vlanDei);
break;
case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
b.append((uint8_t)2);
b.append((uint16_t)rules[i].v.etherType);
break;
case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
case ZT_NETWORK_RULE_MATCH_MAC_DEST:
b.append((uint8_t)6);
b.append(rules[i].v.mac,6);
break;
case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
b.append((uint8_t)5);
b.append(&(rules[i].v.ipv4.ip),4);
b.append((uint8_t)rules[i].v.ipv4.mask);
break;
case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
b.append((uint8_t)17);
b.append(rules[i].v.ipv6.ip,16);
b.append((uint8_t)rules[i].v.ipv6.mask);
break;
case ZT_NETWORK_RULE_MATCH_IP_TOS:
b.append((uint8_t)1);
b.append((uint8_t)rules[i].v.ipTos);
break;
case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
b.append((uint8_t)1);
b.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:
b.append((uint8_t)4);
b.append((uint16_t)rules[i].v.port[0]);
b.append((uint16_t)rules[i].v.port[1]);
break;
case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
b.append((uint8_t)8);
b.append((uint64_t)rules[i].v.characteristics);
break;
case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
b.append((uint8_t)4);
b.append((uint16_t)rules[i].v.frameSize[0]);
b.append((uint16_t)rules[i].v.frameSize[1]);
break;
case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
b.append((uint8_t)8);
b.append((uint32_t)rules[i].v.tcpseq[0]);
b.append((uint32_t)rules[i].v.tcpseq[1]);
break;
}
}
this->com.serialize(b);
b.append((uint16_t)0); // extended bytes, currently 0 since unused
}
template<unsigned int C>
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
{
memset(this,0,sizeof(NetworkConfig));
unsigned int p = startAt;
if (b.template at<uint16_t>(p) != 1)
throw std::invalid_argument("unrecognized version");
p += 2;
networkId = b.template at<uint64_t>(p); p += 8;
timestamp = b.template at<uint64_t>(p); p += 8;
revision = b.template at<uint64_t>(p); p += 8;
issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
multicastLimit = (unsigned int)b.template at<uint32_t>(p); p += 4;
flags = (unsigned int)b.template at<uint32_t>(p); p += 4;
type = (ZT_VirtualNetworkType)b[p++];
unsigned int nl = (unsigned int)b[p++];
memcpy(this->name,b.field(p,nl),std::min(nl,(unsigned int)ZT_MAX_NETWORK_SHORT_NAME_LENGTH));
p += nl;
// _name will always be null terminated since field size is ZT_MAX_NETWORK_SHORT_NAME_LENGTH + 1
specialistCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
if (specialistCount > ZT_MAX_NETWORK_SPECIALISTS)
throw std::invalid_argument("overflow (specialists)");
for(unsigned int i=0;i<specialistCount;++i) { for(unsigned int i=0;i<specialistCount;++i) {
if ((specialists[i] & 0xffffffffffULL) == aint) { specialists[i] = b.template at<uint64_t>(p); p += 8;
specialists[i] |= f;
return true;
}
}
if (specialistCount < ZT_MAX_NETWORK_SPECIALISTS) {
specialists[specialistCount++] = f | aint;
return true;
}
return false;
} }
const Capability *capability(const uint32_t id) const routeCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
{ if (routeCount > ZT_MAX_NETWORK_ROUTES)
for(unsigned int i=0;i<capabilityCount;++i) { throw std::invalid_argument("overflow (routes)");
if (capabilities[i].id() == id) for(unsigned int i=0;i<routeCount;++i) {
return &(capabilities[i]); p += reinterpret_cast<InetAddress *>(&(routes[i].target))->deserialize(b,p);
} p += reinterpret_cast<InetAddress *>(&(routes[i].via))->deserialize(b,p);
return (Capability *)0;
} }
const Tag *tag(const uint32_t id) const staticIpCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
{ if (staticIpCount > ZT_MAX_ZT_ASSIGNED_ADDRESSES)
for(unsigned int i=0;i<tagCount;++i) { throw std::invalid_argument("overflow (static IPs)");
if (tags[i].id() == id) for(unsigned int i=0;i<staticIpCount;++i) {
return &(tags[i]); p += staticIps[i].deserialize(b,p);
} }
return (Tag *)0;
pinnedCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
if (pinnedCount > ZT_MAX_NETWORK_PINNED)
throw std::invalid_argument("overflow (static addresses)");
for(unsigned int i=0;i<pinnedCount;++i) {
pinned[i].zt.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
p += pinned[i].phy.deserialize(b,p);
} }
ruleCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
if (ruleCount > ZT_MAX_NETWORK_RULES)
throw std::invalid_argument("overflow (rules)");
for(unsigned int i=0;i<ruleCount;++i) {
rules[i].t = (uint8_t)b[p++];
unsigned int rlen = (unsigned int)b[p++];
switch((ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f)) {
//case ZT_NETWORK_RULE_ACTION_DROP:
//case ZT_NETWORK_RULE_ACTION_ACCEPT:
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: {
Address tmp;
tmp.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
rules[i].v.zt = tmp.toInt();
} break;
case ZT_NETWORK_RULE_MATCH_VLAN_ID:
rules[i].v.vlanId = b.template at<uint16_t>(p);
break;
case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
rules[i].v.vlanPcp = (uint8_t)b[p];
break;
case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
rules[i].v.vlanDei = (uint8_t)b[p];
break;
case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
rules[i].v.etherType = b.template at<uint16_t>(p);
break;
case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
case ZT_NETWORK_RULE_MATCH_MAC_DEST:
memcpy(rules[i].v.mac,b.field(p,6),6);
break;
case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
memcpy(&(rules[i].v.ipv4.ip),b.field(p,4),4);
rules[i].v.ipv4.mask = (uint8_t)b[p+4];
break;
case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
memcpy(rules[i].v.ipv6.ip,b.field(p,16),16);
rules[i].v.ipv6.mask = (uint8_t)b[p+16];
break;
case ZT_NETWORK_RULE_MATCH_IP_TOS:
rules[i].v.ipTos = (uint8_t)b[p];
break;
case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
rules[i].v.ipProtocol = (uint8_t)b[p];
break;
case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
rules[i].v.port[0] = b.template at<uint16_t>(p);
rules[i].v.port[1] = b.template at<uint16_t>(p+2);
break;
case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
rules[i].v.characteristics = b.template at<uint64_t>(p);
break;
case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
rules[i].v.frameSize[0] = b.template at<uint16_t>(p);
rules[i].v.frameSize[1] = b.template at<uint16_t>(p+2);
break;
case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
rules[i].v.tcpseq[0] = b.template at<uint32_t>(p);
rules[i].v.tcpseq[1] = b.template at<uint32_t>(p + 4);
break;
}
p += rlen;
}
p += this->com.deserialize(b,p);
p += b.template at<uint16_t>(p) + 2;
return (p - startAt);
}
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
void fromDictionary(const char *ds,unsigned int dslen);
#endif
/* /*
inline void dump() const inline void dump() const
{ {
printf("networkId==%.16llx\n",networkId); printf("networkId==%.16llx\n",networkId);
printf("timestamp==%llu\n",timestamp); printf("timestamp==%llu\n",timestamp);
printf("credentialTimeMaxDelta==%llu\n",credentialTimeMaxDelta);
printf("revision==%llu\n",revision); printf("revision==%llu\n",revision);
printf("issuedTo==%.10llx\n",issuedTo.toInt()); printf("issuedTo==%.10llx\n",issuedTo.toInt());
printf("multicastLimit==%u\n",multicastLimit); printf("multicastLimit==%u\n",multicastLimit);
@@ -411,14 +623,17 @@ public:
printf(" specialists[%u]==%.16llx\n",i,specialists[i]); printf(" specialists[%u]==%.16llx\n",i,specialists[i]);
printf("routeCount==%u\n",routeCount); printf("routeCount==%u\n",routeCount);
for(unsigned int i=0;i<routeCount;++i) { 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].target==%s\n",reinterpret_cast<const struct sockaddr_storage *>(&(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].via==%s\n",reinterpret_cast<const struct sockaddr_storage *>(&(routes[i].via))->toString().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); printf("staticIpCount==%u\n",staticIpCount);
for(unsigned int i=0;i<staticIpCount;++i) for(unsigned int i=0;i<staticIpCount;++i)
printf(" staticIps[i]==%s\n",staticIps[i].toString().c_str()); 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].zt->toString().c_str());
}
printf("ruleCount==%u\n",ruleCount); printf("ruleCount==%u\n",ruleCount);
printf("name==%s\n",name); printf("name==%s\n",name);
printf("com==%s\n",com.toString().c_str()); printf("com==%s\n",com.toString().c_str());
@@ -435,11 +650,6 @@ 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
*/ */
@@ -450,16 +660,16 @@ public:
*/ */
Address issuedTo; Address issuedTo;
/**
* Flags (64-bit)
*/
uint64_t flags;
/** /**
* Maximum number of recipients per multicast (not including active bridges) * Maximum number of recipients per multicast (not including active bridges)
*/ */
unsigned int multicastLimit; unsigned int multicastLimit;
/**
* Flags (32-bit)
*/
unsigned int flags;
/** /**
* Number of specialists * Number of specialists
*/ */
@@ -485,16 +695,6 @@ public:
*/ */
unsigned int ruleCount; unsigned int ruleCount;
/**
* Number of capabilities
*/
unsigned int capabilityCount;
/**
* Number of tags
*/
unsigned int tagCount;
/** /**
* Specialist devices * Specialist devices
* *
@@ -514,20 +714,21 @@ public:
InetAddress staticIps[ZT_MAX_ZT_ASSIGNED_ADDRESSES]; InetAddress staticIps[ZT_MAX_ZT_ASSIGNED_ADDRESSES];
/** /**
* Base network rules * Pinned devices with physical address hints
*
* 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];
/** /**
* Network type (currently just public or private) * Network type (currently just public or private)
*/ */

View File

@@ -22,15 +22,15 @@
#include <stdint.h> #include <stdint.h>
#include "Constants.hpp" #include "Constants.hpp"
#include "Dictionary.hpp" #include "InetAddress.hpp"
#include "NetworkConfig.hpp" #include "Address.hpp"
#include "Identity.hpp"
#include "NetworkConfigRequestMetaData.hpp"
#include "Buffer.hpp"
namespace ZeroTier { namespace ZeroTier {
class RuntimeEnvironment; class RuntimeEnvironment;
class Identity;
class Address;
struct InetAddress;
/** /**
* Interface for network controller implementations * Interface for network controller implementations
@@ -67,16 +67,16 @@ public:
* @param identity Originating peer ZeroTier identity * @param identity Originating peer ZeroTier identity
* @param nwid 64-bit network ID * @param nwid 64-bit network ID
* @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 * @param result Buffer to receive serialized network configuration data (any existing data in buffer is preserved)
* @return Returns NETCONF_QUERY_OK if result 'nc' is valid, or an error code on error * @return Returns NETCONF_QUERY_OK if result dictionary is valid, or an error code on error
*/ */
virtual NetworkController::ResultCode doNetworkConfigRequest( virtual NetworkController::ResultCode doNetworkConfigRequest(
const InetAddress &fromAddr, const InetAddress &fromAddr,
const Identity &signingId, const Identity &signingId,
const Identity &identity, const Identity &identity,
uint64_t nwid, uint64_t nwid,
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData, const NetworkConfigRequestMetaData &metaData,
NetworkConfig &nc) = 0; Buffer<8194> &result) = 0;
}; };
} // namespace ZeroTier } // namespace ZeroTier

View File

@@ -37,6 +37,7 @@
#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};
@@ -71,14 +72,10 @@ Node::Node(
_prngStreamPtr(0), _prngStreamPtr(0),
_now(now), _now(now),
_lastPingCheck(0), _lastPingCheck(0),
_lastHousekeepingRun(0), _lastHousekeepingRun(0)
_relayPolicy(ZT_RELAY_POLICY_TRUSTED)
{ {
_online = false; _online = false;
memset(_expectingRepliesToBucketPtr,0,sizeof(_expectingRepliesToBucketPtr));
memset(_expectingRepliesTo,0,sizeof(_expectingRepliesTo));
// 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];
@@ -111,7 +108,9 @@ Node::Node(
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;
@@ -119,9 +118,6 @@ Node::Node(
throw; throw;
} }
if (RR->topology->amRoot())
_relayPolicy = ZT_RELAY_POLICY_ALWAYS;
postEvent(ZT_EVENT_UP); postEvent(ZT_EVENT_UP);
} }
@@ -131,11 +127,12 @@ 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
@@ -176,10 +173,11 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
class _PingPeersThatNeedPing class _PingPeersThatNeedPing
{ {
public: public:
_PingPeersThatNeedPing(const RuntimeEnvironment *renv,uint64_t now) : _PingPeersThatNeedPing(const RuntimeEnvironment *renv,uint64_t now,const std::vector<NetworkConfig::Relay> &relays) :
lastReceiveFromUpstream(0), lastReceiveFromUpstream(0),
RR(renv), RR(renv),
_now(now), _now(now),
_relays(relays),
_world(RR->topology->world()) _world(RR->topology->world())
{ {
} }
@@ -210,6 +208,25 @@ public:
} }
} }
if (!upstream) {
// If I am a root server, only ping other root servers -- roots don't ping "down"
// since that would just be a waste of bandwidth and could potentially cause route
// flapping in Cluster mode.
if (RR->topology->amRoot())
return;
// 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;
}
}
}
if (upstream) { if (upstream) {
// "Upstream" devices are roots and relays and get special treatment -- they stay alive // "Upstream" devices are roots and relays and get special treatment -- they stay alive
// forever and we try to keep (if available) both IPv4 and IPv6 channels open to them. // forever and we try to keep (if available) both IPv4 and IPv6 channels open to them.
@@ -237,19 +254,20 @@ public:
// way whatsoever. This will e.g. find network preferred relays that lack // way whatsoever. This will e.g. find network preferred relays that lack
// stable endpoints by using root servers. // stable endpoints by using root servers.
Packet outp(p->address(),RR->identity.address(),Packet::VERB_NOP); Packet outp(p->address(),RR->identity.address(),Packet::VERB_NOP);
RR->sw->send(outp,true); RR->sw->send(outp,true,0);
} }
lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream); lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);
} else if (p->isActive(_now)) { } else if (p->activelyTransferringFrames(_now)) {
// Normal nodes get their preferred link kept alive if the node has generated frame traffic recently // Normal nodes get their preferred link kept alive if the node has generated frame traffic recently
p->doPingAndKeepalive(_now,-1); p->doPingAndKeepalive(_now,0);
} }
} }
private: private:
const RuntimeEnvironment *RR; const RuntimeEnvironment *RR;
uint64_t _now; uint64_t _now;
const std::vector<NetworkConfig::Relay> &_relays;
World _world; World _world;
}; };
@@ -265,20 +283,27 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
_lastPingCheck = now; _lastPingCheck = now;
// Get relays and networks that need config without leaving the mutex locked // Get relays and networks that need config without leaving the 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); _PingPeersThatNeedPing pfunc(RR,now,networkRelays);
RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc); RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc);
// Update online status, post status change as event // Update online status, post status change as event
@@ -324,12 +349,6 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
return ZT_RESULT_OK; return ZT_RESULT_OK;
} }
ZT_ResultCode Node::setRelayPolicy(enum ZT_RelayPolicy rp)
{
_relayPolicy = rp;
return ZT_RESULT_OK;
}
ZT_ResultCode Node::join(uint64_t nwid,void *uptr) ZT_ResultCode Node::join(uint64_t nwid,void *uptr)
{ {
Mutex::Lock _l(_networks_m); Mutex::Lock _l(_networks_m);
@@ -419,16 +438,15 @@ ZT_PeerList *Node::peers() const
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->isRoot(pi->second->identity()) ? ZT_PEER_ROLE_ROOT : ZT_PEER_ROLE_LEAF;
std::vector< std::pair< SharedPtr<Path>,bool > > paths(pi->second->paths(_now)); std::vector<Path> paths(pi->second->paths());
SharedPtr<Path> bestp(pi->second->getBestPath(_now,false)); Path *bestPath = pi->second->getBestPath(_now);
p->pathCount = 0; p->pathCount = 0;
for(std::vector< std::pair< SharedPtr<Path>,bool > >::iterator path(paths.begin());path!=paths.end();++path) { for(std::vector<Path>::iterator path(paths.begin());path!=paths.end();++path) {
memcpy(&(p->paths[p->pathCount].address),&(path->first->address()),sizeof(struct sockaddr_storage)); memcpy(&(p->paths[p->pathCount].address),&(path->address()),sizeof(struct sockaddr_storage));
p->paths[p->pathCount].lastSend = path->first->lastOut(); p->paths[p->pathCount].lastSend = path->lastSend();
p->paths[p->pathCount].lastReceive = path->first->lastIn(); p->paths[p->pathCount].lastReceive = path->lastReceived();
p->paths[p->pathCount].expired = path->second; p->paths[p->pathCount].active = path->active(_now) ? 1 : 0;
p->paths[p->pathCount].preferred = (path->first == bestp) ? 1 : 0; p->paths[p->pathCount].preferred = ((bestPath)&&(*path == *bestPath)) ? 1 : 0;
p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust(path->first->address());
++p->pathCount; ++p->pathCount;
} }
} }
@@ -524,7 +542,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); RR->sw->send(outp,true,0);
} }
} 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
@@ -620,6 +638,18 @@ 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/ */
/****************************************************************************/ /****************************************************************************/
@@ -715,11 +745,6 @@ void Node::postCircuitTestReport(const ZT_CircuitTestReport *report)
(reinterpret_cast<void (*)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)>((*i)->_internalPtr))(reinterpret_cast<ZT_Node *>(this),*i,report); (reinterpret_cast<void (*)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)>((*i)->_internalPtr))(reinterpret_cast<ZT_Node *>(this),*i,report);
} }
void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
{
RR->topology->setTrustedPaths(reinterpret_cast<const InetAddress *>(networks),ids,count);
}
} // namespace ZeroTier } // namespace ZeroTier
/****************************************************************************/ /****************************************************************************/
@@ -810,15 +835,6 @@ enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,vol
} }
} }
enum ZT_ResultCode ZT_Node_setRelayPolicy(ZT_Node *node,enum ZT_RelayPolicy rp)
{
try {
return reinterpret_cast<ZeroTier::Node *>(node)->setRelayPolicy(rp);
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
}
enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr) enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr)
{ {
try { try {
@@ -998,10 +1014,10 @@ void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs)
} catch ( ... ) {} } catch ( ... ) {}
} }
void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count) void ZT_Node_backgroundThreadMain(ZT_Node *node)
{ {
try { try {
reinterpret_cast<ZeroTier::Node *>(node)->setTrustedPaths(networks,ids,count); reinterpret_cast<ZeroTier::Node *>(node)->backgroundThreadMain();
} catch ( ... ) {} } catch ( ... ) {}
} }

View File

@@ -44,10 +44,6 @@
#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
namespace ZeroTier { namespace ZeroTier {
/** /**
@@ -91,7 +87,6 @@ public:
unsigned int frameLength, unsigned int frameLength,
volatile uint64_t *nextBackgroundTaskDeadline); volatile uint64_t *nextBackgroundTaskDeadline);
ZT_ResultCode processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline); ZT_ResultCode processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
ZT_ResultCode setRelayPolicy(enum ZT_RelayPolicy rp);
ZT_ResultCode join(uint64_t nwid,void *uptr); ZT_ResultCode join(uint64_t nwid,void *uptr);
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);
@@ -122,9 +117,18 @@ 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() * @return Time as of last call to run()
*/ */
@@ -244,43 +248,26 @@ public:
*/ */
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 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); }
/**
* @return True if we appear to be online
*/
inline bool online() const throw() { return _online; } inline bool online() const throw() { return _online; }
inline ZT_RelayPolicy relayPolicy() const { return _relayPolicy; }
#ifdef ZT_TRACE #ifdef ZT_TRACE
void postTrace(const char *module,unsigned int line,const char *fmt,...); void postTrace(const char *module,unsigned int line,const char *fmt,...);
#endif #endif
/**
* @return Next 64-bit random number (not for cryptographic use)
*/
uint64_t prng(); uint64_t prng();
/**
* Post a circuit test report to any listeners for a given test ID
*
* @param report Report (includes test ID)
*/
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);
/**
* Register that we are expecting a reply to a packet ID
*
* @param packetId Packet ID to expect reply to
*/
inline void expectReplyTo(const uint64_t packetId)
{
const unsigned long bucket = (unsigned long)(packetId & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
_expectingRepliesTo[bucket][_expectingRepliesToBucketPtr[bucket]++ & ZT_EXPECTING_REPLIES_BUCKET_MASK2] = packetId;
}
/**
* Check whether a given packet ID is something we are expecting a reply to
*
* @param packetId Packet ID to check
* @return True if we're expecting a reply
*/
inline bool expectingReplyTo(const uint64_t packetId) const
{
const unsigned long bucket = (unsigned long)(packetId & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
for(unsigned long i=0;i<=ZT_EXPECTING_REPLIES_BUCKET_MASK2;++i) {
if (_expectingRepliesTo[bucket][i] == packetId)
return true;
}
return false;
}
private: private:
inline SharedPtr<Network> _network(uint64_t nwid) const inline SharedPtr<Network> _network(uint64_t nwid) const
@@ -298,9 +285,6 @@ private:
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
uint8_t _expectingRepliesToBucketPtr[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1];
uint64_t _expectingRepliesTo[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1][ZT_EXPECTING_REPLIES_BUCKET_MASK2 + 1];
ZT_DataStoreGetFunction _dataStoreGetFunction; ZT_DataStoreGetFunction _dataStoreGetFunction;
ZT_DataStorePutFunction _dataStorePutFunction; ZT_DataStorePutFunction _dataStorePutFunction;
ZT_WirePacketSendFunction _wirePacketSendFunction; ZT_WirePacketSendFunction _wirePacketSendFunction;
@@ -327,7 +311,6 @@ private:
uint64_t _now; uint64_t _now;
uint64_t _lastPingCheck; uint64_t _lastPingCheck;
uint64_t _lastHousekeepingRun; uint64_t _lastHousekeepingRun;
ZT_RelayPolicy _relayPolicy;
bool _online; bool _online;
}; };

View File

@@ -21,9 +21,8 @@
#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 {
@@ -31,7 +30,7 @@ void OutboundMulticast::init(
const RuntimeEnvironment *RR, const RuntimeEnvironment *RR,
uint64_t timestamp, uint64_t timestamp,
uint64_t nwid, uint64_t nwid,
bool disableCompression, const CertificateOfMembership *com,
unsigned int limit, unsigned int limit,
unsigned int gatherLimit, unsigned int gatherLimit,
const MAC &src, const MAC &src,
@@ -40,25 +39,16 @@ 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", TRACE(">>MC %.16llx INIT %.16llx/%s limit %u gatherLimit %u from %s to %s length %u com==%d",
(unsigned long long)this, (unsigned long long)this,
nwid, nwid,
dest.toString().c_str(), dest.toString().c_str(),
@@ -66,36 +56,58 @@ 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);
*/ */
_packet.setSource(RR->identity.address()); _packetNoCom.setSource(RR->identity.address());
_packet.setVerb(Packet::VERB_MULTICAST_FRAME); _packetNoCom.setVerb(Packet::VERB_MULTICAST_FRAME);
_packet.append((uint64_t)nwid); _packetNoCom.append((uint64_t)nwid);
_packet.append(flags); _packetNoCom.append(flags);
if (gatherLimit) _packet.append((uint32_t)gatherLimit); if (gatherLimit) _packetNoCom.append((uint32_t)gatherLimit);
if (src) src.appendTo(_packet); if (src) src.appendTo(_packetNoCom);
dest.mac().appendTo(_packet); dest.mac().appendTo(_packetNoCom);
_packet.append((uint32_t)dest.adi()); _packetNoCom.append((uint32_t)dest.adi());
_packet.append((uint16_t)etherType); _packetNoCom.append((uint16_t)etherType);
_packet.append(payload,_frameLen); _packetNoCom.append(payload,len);
if (!disableCompression) _packetNoCom.compress();
_packet.compress();
memcpy(_frameData,payload,_frameLen); if (com) {
_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)
{ {
const SharedPtr<Network> nw(RR->node->network(_nwid)); if (_haveCom) {
Address toAddr2(toAddr); SharedPtr<Peer> peer(RR->topology->getPeer(toAddr));
if ((nw)&&(nw->filterOutgoingPacket(true,RR->identity.address(),toAddr2,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) { if ( (!peer) || (peer->needsOurNetworkMembershipCertificate(_nwid,RR->node->now(),true)) ) {
//TRACE(">>MC %.16llx -> %s",(unsigned long long)this,toAddr.toString().c_str()); //TRACE(">>MC %.16llx -> %s (with COM)",(unsigned long long)this,toAddr.toString().c_str());
_packet.newInitializationVector(); _packetWithCom.newInitializationVector();
_packet.setDestination(toAddr2); _packetWithCom.setDestination(toAddr);
RR->node->expectReplyTo(_packet.packetId()); RR->sw->send(_packetWithCom,true,_nwid);
RR->sw->send(_packet,true); return;
} }
} }
//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

View File

@@ -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 disableCompression Disable compression of frame payload * @param com Certificate of membership or NULL if none available
* @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,
bool disableCompression, const CertificateOfMembership *com,
unsigned int limit, unsigned int limit,
unsigned int gatherLimit, unsigned int gatherLimit,
const MAC &src, const MAC &src,
@@ -127,22 +127,17 @@ 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 { } else return false;
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;
unsigned int _frameLen; Packet _packetNoCom;
unsigned int _etherType; Packet _packetWithCom;
Packet _packet;
std::vector<Address> _alreadySentTo; std::vector<Address> _alreadySentTo;
uint8_t _frameData[ZT_MAX_MTU]; bool _haveCom;
}; };
} // namespace ZeroTier } // namespace ZeroTier

View File

@@ -22,7 +22,7 @@ namespace ZeroTier {
const unsigned char Packet::ZERO_KEY[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; const unsigned char Packet::ZERO_KEY[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
#ifdef ZT_TRACE //#ifdef ZT_TRACE
const char *Packet::verbString(Verb v) const char *Packet::verbString(Verb v)
throw() throw()
@@ -38,16 +38,15 @@ const char *Packet::verbString(Verb v)
case VERB_EXT_FRAME: return "EXT_FRAME"; case VERB_EXT_FRAME: return "EXT_FRAME";
case VERB_ECHO: return "ECHO"; case VERB_ECHO: return "ECHO";
case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE"; case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
case VERB_NETWORK_CREDENTIALS: return "NETWORK_CREDENTIALS"; case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE";
case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST"; case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST";
case VERB_NETWORK_CONFIG: return "NETWORK_CONFIG_REFRESH"; case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH";
case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER"; case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER";
case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS"; case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS";
case VERB_CIRCUIT_TEST: return "CIRCUIT_TEST"; case VERB_CIRCUIT_TEST: return "CIRCUIT_TEST";
case VERB_CIRCUIT_TEST_REPORT: return "CIRCUIT_TEST_REPORT"; case VERB_CIRCUIT_TEST_REPORT: return "CIRCUIT_TEST_REPORT";
case VERB_REQUEST_PROOF_OF_WORK: return "REQUEST_PROOF_OF_WORK"; case VERB_REQUEST_PROOF_OF_WORK: return "REQUEST_PROOF_OF_WORK";
case VERB_USER_MESSAGE: return "USER_MESSAGE";
} }
return "(unknown)"; return "(unknown)";
} }
@@ -69,7 +68,7 @@ const char *Packet::errorString(ErrorCode e)
return "(unknown)"; return "(unknown)";
} }
#endif // ZT_TRACE //#endif // ZT_TRACE
void Packet::armor(const void *key,bool encryptPayload) void Packet::armor(const void *key,bool encryptPayload)
{ {

View File

@@ -34,11 +34,7 @@
#include "Utils.hpp" #include "Utils.hpp"
#include "Buffer.hpp" #include "Buffer.hpp"
#ifdef ZT_USE_SYSTEM_LZ4
#include <lz4.h>
#else
#include "../ext/lz4/lz4.h" #include "../ext/lz4/lz4.h"
#endif
/** /**
* Protocol version -- incremented only for major changes * Protocol version -- incremented only for major changes
@@ -51,23 +47,14 @@
* + 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
* + BREAKING CHANGE: New identity format based on hashcash design * + New identity format based on hashcash design
* 5 - 1.1.0 ... 1.1.5 * 5 - 1.1.0 ... CURRENT
* + 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
* + Network configuration format revisions including binary values
* 7 - 1.1.10 -- 1.2.0
* + Introduce trusted paths for local SDN use
* 8 - 1.2.0 -- CURRENT
* + Multipart network configurations for large network configs
* + Tags and Capabilities
* + Inline push of CertificateOfMembership deprecated
* + Certificates of representation for federation and mesh
*/ */
#define ZT_PROTO_VERSION 8 #define ZT_PROTO_VERSION 5
/** /**
* Minimum supported protocol version * Minimum supported protocol version
@@ -106,21 +93,10 @@
#define ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 1 #define ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 1
/** /**
* Cipher suite: NONE * DEPRECATED payload encrypted flag, will be removed for re-use soon.
* *
* This differs from POLY1305/NONE in that *no* crypto is done, not even * This has been replaced by the two-bit cipher suite selection field where
* authentication. This is for trusted local LAN interconnects for internal * a value of 0 indicates unencrypted (but authenticated) messages.
* SDN use within a data center.
*
* For this mode the MAC field becomes a trusted path ID and must match the
* configured ID of a trusted path or the packet is discarded.
*/
#define ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH 2
/**
* DEPRECATED payload encrypted flag, may be re-used in the future.
*
* This has been replaced by the three-bit cipher suite selection field.
*/ */
#define ZT_PROTO_FLAG_ENCRYPTED 0x80 #define ZT_PROTO_FLAG_ENCRYPTED 0x80
@@ -307,7 +283,6 @@
#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)
@@ -355,7 +330,7 @@ namespace ZeroTier {
* <[5] destination ZT address> * <[5] destination ZT address>
* <[5] source ZT address> * <[5] source ZT address>
* <[1] flags/cipher/hops> * <[1] flags/cipher/hops>
* <[8] 64-bit MAC (or trusted path ID in trusted path mode)> * <[8] 64-bit MAC>
* [... -- begin encryption envelope -- ...] * [... -- begin encryption envelope -- ...]
* <[1] encrypted flags (MS 3 bits) and verb (LS 5 bits)> * <[1] encrypted flags (MS 3 bits) and verb (LS 5 bits)>
* [... verb-specific payload ...] * [... verb-specific payload ...]
@@ -528,7 +503,7 @@ public:
/** /**
* No operation (ignored, no reply) * No operation (ignored, no reply)
*/ */
VERB_NOP = 0x00, VERB_NOP = 0,
/** /**
* Announcement of a node's existence: * Announcement of a node's existence:
@@ -539,7 +514,7 @@ public:
* <[8] timestamp (ms since epoch)> * <[8] timestamp (ms since epoch)>
* <[...] binary serialized identity (see Identity)> * <[...] binary serialized identity (see Identity)>
* <[1] destination address type> * <[1] destination address type>
* [<[...] destination address to which packet was sent>] * [<[...] destination address>]
* <[8] 64-bit world ID of current world> * <[8] 64-bit world ID of current world>
* <[8] 64-bit timestamp of current world> * <[8] 64-bit timestamp of current world>
* *
@@ -571,7 +546,7 @@ public:
* *
* ERROR has no payload. * ERROR has no payload.
*/ */
VERB_HELLO = 0x01, VERB_HELLO = 1,
/** /**
* Error response: * Error response:
@@ -580,7 +555,7 @@ public:
* <[1] error code> * <[1] error code>
* <[...] error-dependent payload> * <[...] error-dependent payload>
*/ */
VERB_ERROR = 0x02, VERB_ERROR = 2,
/** /**
* Success response: * Success response:
@@ -588,29 +563,25 @@ public:
* <[8] in-re packet ID> * <[8] in-re packet ID>
* <[...] request-specific payload> * <[...] request-specific payload>
*/ */
VERB_OK = 0x03, VERB_OK = 3,
/** /**
* 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 must be tolerated, which is easy since they'll have info you * These should be discarded.
* already have.
* *
* If the address is not found, no response is generated. The semantics * If the address is not found, no response is generated. WHOIS requests
* of WHOIS is similar to ARP and NDP in that persistent retrying can * will time out much like ARP requests and similar do in L2.
* be performed.
*/ */
VERB_WHOIS = 0x04, VERB_WHOIS = 4,
/** /**
* Relay-mediated NAT traversal or firewall punching initiation: * Meet another node at a given protocol address:
* <[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>
@@ -624,9 +595,18 @@ public:
* *
* 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 = 0x05, VERB_RENDEZVOUS = 5,
/** /**
* ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME): * ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME):
@@ -642,44 +622,31 @@ 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 = 0x06, VERB_FRAME = 6,
/** /**
* 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 attached (DEPRECATED) * 0x01 - Certificate of network membership is attached
* 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)
* *
* Subtypes (0..7): * An extended frame carries full MAC addressing, making them a
* 0x0 - Normal frame (bridging can be determined by checking MAC) * superset of VERB_FRAME. They're used for bridging or when we
* 0x1 - TEEd outbound frame * want to attach a certificate since FRAME does not support that.
* 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)
* *
* An extended frame carries full MAC addressing, making it a * Multicast frames may not be sent as EXT_FRAME.
* 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.
* *
* OK payload (if ACK flag is set): * ERROR may be generated if a membership certificate is needed for a
* <[8] 64-bit network ID> * closed network. Payload will be network ID.
*/ */
VERB_EXT_FRAME = 0x07, VERB_EXT_FRAME = 7,
/** /**
* ECHO request (a.k.a. ping): * ECHO request (a.k.a. ping):
@@ -689,7 +656,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 = 0x08, VERB_ECHO = 8,
/** /**
* Announce interest in multicast group(s): * Announce interest in multicast group(s):
@@ -703,116 +670,77 @@ 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.
* *
* VERB_NETWORK_CREDENTIALS should be pushed along with this, especially * It is recommended that NETWORK_MEMBERSHIP_CERTIFICATE pushes be sent
* if using upstream (e.g. root) nodes as multicast databases. This allows * along with MULTICAST_LIKE when pushing LIKEs to peers that do not
* GATHERs to be authenticated. * share a network membership (such as root servers), since this can be
* 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 = 0x09, VERB_MULTICAST_LIKE = 9,
/** /**
* Network credentials push: * Network member certificate replication/push:
* <[...] serialized certificate of membership> * <[...] serialized certificate of membership>
* [<[...] additional 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>
* *
* This can be sent by anyone at any time to push network credentials. * This is sent in response to ERROR_NEED_MEMBERSHIP_CERTIFICATE and may
* These will of course only be accepted if they are properly signed. * be pushed at any other time to keep exchanged certificates up to date.
* 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_CREDENTIALS = 0x0a, VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10,
/** /**
* 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. * providing it. If the optional revision is included, a response is
* * 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 chunk> * <[2] 16-bit length of network configuration dictionary>
* <[...] network configuration dictionary (may be incomplete)> * <[...] network configuration dictionary>
* [ ... 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>
* *
* The chunk signature signs the entire payload of the OK response. * OK returns a Dictionary (string serialized) containing the network's
* Currently only one signature type is supported: ed25519 (1). * configuration and IP address assignment information for the querying
* 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.
* *
* Each config chunk is signed to prevent memory exhaustion or * When a new network configuration is received, another config request
* traffic crowding DOS attacks against config fragment assembly. * should be sent with the new netconf's revision. This confirms receipt
* * and also causes any subsequent changes to rapidly propagate as this
* If the packet is from the network controller it is permitted to end * cycle will repeat until there are no changes. This is optional but
* before the config update ID or other chunking related or signature * recommended behavior.
* 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 = 0x0b, VERB_NETWORK_CONFIG_REQUEST = 11,
/** /**
* Network configuration data push: * Network configuration refresh request:
* <[8] 64-bit network ID> * <[...] array of 64-bit network IDs>
* <[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 is a direct push variant for network config updates. It otherwise * This can be sent by the network controller to inform a node that it
* carries the same payload as OK(NETWORK_CONFIG_REQUEST) and has the same * should now make a NETWORK_CONFIG_REQUEST.
* semantics.
* *
* The legacy mode missing the additional chunking fields is not supported * It does not generate an OK or ERROR message, and is treated only as
* here. * a hint to refresh now.
*
* 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 = 0x0c, VERB_NETWORK_CONFIG_REFRESH = 12,
/** /**
* Request endpoints for multicast distribution: * Request endpoints for multicast distribution:
@@ -824,7 +752,7 @@ public:
* [<[...] network certificate of membership>] * [<[...] network certificate of membership>]
* *
* Flags: * Flags:
* 0x01 - COM is attached * 0x01 - Network certificate of membership 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
@@ -834,9 +762,6 @@ 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>
@@ -848,12 +773,13 @@ 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 = 0x0d, VERB_MULTICAST_GATHER = 13,
/** /**
* 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)>
@@ -862,7 +788,7 @@ public:
* <[...] ethernet payload> * <[...] ethernet payload>
* *
* Flags: * Flags:
* 0x01 - Network certificate of membership attached (DEPRECATED) * 0x01 - Network certificate of membership is attached
* 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
* *
@@ -877,11 +803,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 (DEPRECATED)>] * [<[...] network certficate of membership>]
* [<[...] 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 (DEPRECATED) * 0x01 - OK includes certificate of network membership
* 0x02 - OK includes implicit gather results * 0x02 - OK includes implicit gather results
* *
* ERROR response payload: * ERROR response payload:
@@ -889,9 +815,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 = 0x0e, VERB_MULTICAST_FRAME = 14,
// 0x0f is reserved for an old deprecated message
/** /**
* Push of potential endpoints for direct communication: * Push of potential endpoints for direct communication:
@@ -927,7 +851,7 @@ public:
* *
* OK and ERROR are not generated. * OK and ERROR are not generated.
*/ */
VERB_PUSH_DIRECT_PATHS = 0x10, VERB_PUSH_DIRECT_PATHS = 16,
/** /**
* Source-routed circuit test message: * Source-routed circuit test message:
@@ -943,17 +867,21 @@ 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 length of additional fields> * <[2] 16-bit previous hop credential length (including type)>
* [[...] additional fields] * [[1] previous hop credential type]
* [[...] previous hop credential]
* <[...] next hop(s) in path> * <[...] next hop(s) in path>
* *
* Flags: * Flags:
* 0x01 - Report back to originator at all hops * 0x01 - Report back to originator at middle 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)>
@@ -1002,25 +930,25 @@ 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 = 0x11, VERB_CIRCUIT_TEST = 17,
/** /**
* Circuit test hop report: * Circuit test hop report:
* <[8] 64-bit timestamp (echoed from original test)> * <[8] 64-bit timestamp (from original test)>
* <[8] 64-bit test ID (echoed from original test)> * <[8] 64-bit test ID (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 software major version> * <[1] 8-bit reporter major version>
* <[1] 8-bit reporter software minor version> * <[1] 8-bit reporter minor version>
* <[2] 16-bit reporter software revision> * <[2] 16-bit reporter revision>
* <[2] 16-bit reporter OS/platform or 0 if not specified> * <[2] 16-bit reporter OS/platform>
* <[2] 16-bit reporter architecture or 0 if not specified> * <[2] 16-bit reporter architecture>
* <[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> * <[8] 64-bit report flags (set to 0, currently unused)>
* <[8] 64-bit packet ID of received CIRCUIT_TEST packet> * <[8] 64-bit source packet ID>
* <[5] upstream ZeroTier address from which CIRCUIT_TEST was received> * <[5] upstream ZeroTier address from which test was received>
* <[1] 8-bit packet hop count of received CIRCUIT_TEST> * <[1] 8-bit source packet hop count (ZeroTier hop count)>
* <[...] 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 length of additional fields>
@@ -1032,9 +960,6 @@ 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.
@@ -1042,7 +967,7 @@ 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 = 0x12, VERB_CIRCUIT_TEST_REPORT = 18,
/** /**
* Request proof of work: * Request proof of work:
@@ -1085,18 +1010,7 @@ public:
* *
* ERROR has no payload. * ERROR has no payload.
*/ */
VERB_REQUEST_PROOF_OF_WORK = 0x13, VERB_REQUEST_PROOF_OF_WORK = 19
/**
* A message with arbitrary user-definable content:
* <[8] 64-bit arbitrary message type ID>
* [<[...] message payload>]
*
* This can be used to send arbitrary messages over VL1. It generates no
* OK or ERROR and has no special semantics outside of whatever the user
* (via the ZeroTier core API) chooses to give it.
*/
VERB_USER_MESSAGE = 0x14
}; };
/** /**
@@ -1105,39 +1019,39 @@ public:
enum ErrorCode enum ErrorCode
{ {
/* No error, not actually used in transit */ /* No error, not actually used in transit */
ERROR_NONE = 0x00, ERROR_NONE = 0,
/* Invalid request */ /* Invalid request */
ERROR_INVALID_REQUEST = 0x01, ERROR_INVALID_REQUEST = 1,
/* Bad/unsupported protocol version */ /* Bad/unsupported protocol version */
ERROR_BAD_PROTOCOL_VERSION = 0x02, ERROR_BAD_PROTOCOL_VERSION = 2,
/* Unknown object queried */ /* Unknown object queried */
ERROR_OBJ_NOT_FOUND = 0x03, ERROR_OBJ_NOT_FOUND = 3,
/* HELLO pushed an identity whose address is already claimed */ /* HELLO pushed an identity whose address is already claimed */
ERROR_IDENTITY_COLLISION = 0x04, ERROR_IDENTITY_COLLISION = 4,
/* Verb or use case not supported/enabled by this node */ /* Verb or use case not supported/enabled by this node */
ERROR_UNSUPPORTED_OPERATION = 0x05, ERROR_UNSUPPORTED_OPERATION = 5,
/* Network membership certificate update needed */ /* Message to private network rejected -- no unexpired certificate on file */
ERROR_NEED_MEMBERSHIP_CERTIFICATE = 0x06, ERROR_NEED_MEMBERSHIP_CERTIFICATE = 6,
/* Tried to join network, but you're not a member */ /* Tried to join network, but you're not a member */
ERROR_NETWORK_ACCESS_DENIED_ = 0x07, /* extra _ at end to avoid Windows name conflict */ ERROR_NETWORK_ACCESS_DENIED_ = 7, /* extra _ to avoid Windows name conflict */
/* Multicasts to this group are not wanted */ /* Multicasts to this group are not wanted */
ERROR_UNWANTED_MULTICAST = 0x08 ERROR_UNWANTED_MULTICAST = 8
}; };
#ifdef ZT_TRACE //#ifdef ZT_TRACE
static const char *verbString(Verb v) static const char *verbString(Verb v)
throw(); throw();
static const char *errorString(ErrorCode e) static const char *errorString(ErrorCode e)
throw(); throw();
#endif //#endif
template<unsigned int C2> template<unsigned int C2>
Packet(const Buffer<C2> &b) : Packet(const Buffer<C2> &b) :
@@ -1297,6 +1211,7 @@ public:
*/ */
inline unsigned int cipher() const inline unsigned int cipher() const
{ {
// Note: this uses the new cipher spec field, which is incompatible with <1.0.0 peers
return (((unsigned int)(*this)[ZT_PACKET_IDX_FLAGS] & 0x38) >> 3); return (((unsigned int)(*this)[ZT_PACKET_IDX_FLAGS] & 0x38) >> 3);
} }
@@ -1307,30 +1222,12 @@ public:
{ {
unsigned char &b = (*this)[ZT_PACKET_IDX_FLAGS]; unsigned char &b = (*this)[ZT_PACKET_IDX_FLAGS];
b = (b & 0xc7) | (unsigned char)((c << 3) & 0x38); // bits: FFCCCHHH b = (b & 0xc7) | (unsigned char)((c << 3) & 0x38); // bits: FFCCCHHH
// Set DEPRECATED "encrypted" flag -- used by pre-1.0.3 peers // DEPRECATED "encrypted" flag -- used by pre-1.0.3 peers
if (c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012) if (c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
b |= ZT_PROTO_FLAG_ENCRYPTED; b |= ZT_PROTO_FLAG_ENCRYPTED;
else b &= (~ZT_PROTO_FLAG_ENCRYPTED); else b &= (~ZT_PROTO_FLAG_ENCRYPTED);
} }
/**
* Get the trusted path ID for this packet (only meaningful if cipher is trusted path)
*
* @return Trusted path ID (from MAC field)
*/
inline uint64_t trustedPathId() const { return at<uint64_t>(ZT_PACKET_IDX_MAC); }
/**
* Set this packet's trusted path ID and set the cipher spec to trusted path
*
* @param tpid Trusted path ID
*/
inline void setTrusted(const uint64_t tpid)
{
setCipher(ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH);
setAt(ZT_PACKET_IDX_MAC,tpid);
}
/** /**
* 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)
* *
@@ -1374,10 +1271,6 @@ public:
/** /**
* Verify and (if encrypted) decrypt packet * Verify and (if encrypted) decrypt packet
* *
* This does not handle trusted path mode packets and will return false
* for these. These are handled in IncomingPacket if the sending physical
* address and MAC field match a trusted path.
*
* @param key 32-byte key * @param key 32-byte key
* @return False if packet is invalid or failed MAC authenticity check * @return False if packet is invalid or failed MAC authenticity check
*/ */

View File

@@ -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)) {
_lastOut = now; sent(now);
return true; return true;
} }
return false; return false;

View File

@@ -27,9 +27,27 @@
#include "Constants.hpp" #include "Constants.hpp"
#include "InetAddress.hpp" #include "InetAddress.hpp"
#include "SharedPtr.hpp"
#include "AtomicCounter.hpp" // Note: if you change these flags check the logic below. Some of it depends
#include "NonCopyable.hpp" // on these bits being what they are.
/**
* 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()
@@ -41,100 +59,89 @@ namespace ZeroTier {
class RuntimeEnvironment; class RuntimeEnvironment;
/** /**
* A path across the physical network * Base class for paths
*
* The base Path class is an immutable value.
*/ */
class Path : NonCopyable class Path
{
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() :
_lastOut(0), _lastSend(0),
_lastIn(0), _lastPing(0),
_lastTrustEstablishedPacketReceived(0), _lastKeepalive(0),
_lastReceived(0),
_addr(), _addr(),
_localAddress(), _localAddress(),
_flags(0),
_ipScope(InetAddress::IP_SCOPE_NONE) _ipScope(InetAddress::IP_SCOPE_NONE)
{ {
} }
Path(const InetAddress &localAddress,const InetAddress &addr) : Path(const InetAddress &localAddress,const InetAddress &addr) :
_lastOut(0), _lastSend(0),
_lastIn(0), _lastPing(0),
_lastTrustEstablishedPacketReceived(0), _lastKeepalive(0),
_lastReceived(0),
_addr(addr), _addr(addr),
_localAddress(localAddress), _localAddress(localAddress),
_flags(0),
_ipScope(addr.ipScope()) _ipScope(addr.ipScope())
{ {
} }
inline Path &operator=(const Path &p)
{
if (this != &p)
memcpy(this,&p,sizeof(Path));
return *this;
}
/** /**
* Called when a packet is received from this remote path, regardless of content * Called when a packet is sent to this remote path
*
* 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(const uint64_t t) { _lastIn = t; } inline void received(uint64_t t)
{
_lastReceived = t;
_probation = 0;
}
/** /**
* Set time last trusted packet was received (done in Peer::received()) * @param now Current time
* @return True if this path appears active
*/ */
inline void trustedPacketReceived(const uint64_t t) { _lastTrustEstablishedPacketReceived = t; } inline bool active(uint64_t now) const
{
return ( ((now - _lastReceived) < ZT_PATH_ACTIVITY_TIMEOUT) && (_probation < ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION) );
}
/** /**
* Send a packet via this path (last out time is also updated) * Send a packet via this path
* *
* @param RR Runtime environment * @param RR Runtime environment
* @param data Packet data * @param data Packet data
@@ -144,43 +151,117 @@ 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 { return _localAddress; } inline const InetAddress &localAddress() const throw() { 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 { return _addr; } inline const InetAddress &address() const throw() { return _addr; }
/** /**
* @return IP scope -- faster shortcut for address().ipScope() * @return IP scope -- faster shortcut for address().ipScope()
*/ */
inline InetAddress::IpScope ipScope() const { return _ipScope; } inline InetAddress::IpScope ipScope() const throw() { return _ipScope; }
/** /**
* @return True if path has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms * @param f Valuve of ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL and inverse of ZT_PATH_FLAG_CLUSTER_OPTIMAL (both are changed)
*/ */
inline bool trustEstablished(const uint64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); } inline void setClusterSuboptimal(bool f)
/**
* @return Preference rank, higher == better
*/
inline unsigned int preferenceRank() const
{ {
// This causes us to rank paths in order of IP scope rank (see InetAdddress.hpp) but if (f) {
// within each IP scope class to prefer IPv6 over IPv4. _flags = (_flags | ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) & ~ZT_PATH_FLAG_CLUSTER_OPTIMAL;
} 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)
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
* *
@@ -191,6 +272,7 @@ 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()) {
@@ -222,33 +304,60 @@ public:
} }
/** /**
* @return True if path appears alive * @return Current path probation count (for dead path detect)
*/ */
inline bool alive(const uint64_t now) const { return ((now - _lastIn) <= ZT_PATH_ALIVE_TIMEOUT); } inline unsigned int probation() const { return _probation; }
/** /**
* @return True if this path needs a heartbeat * Increase this path's probation violation count (for dead path detect)
*/ */
inline bool needsHeartbeat(const uint64_t now) const { return ((now - _lastOut) >= ZT_PATH_HEARTBEAT_PERIOD); } inline void increaseProbation() { ++_probation; }
/** template<unsigned int C>
* @return Last time we sent something inline void serialize(Buffer<C> &b) const
*/ {
inline uint64_t lastOut() const { return _lastOut; } b.append((uint8_t)2); // version
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>
* @return Last time we received anything inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
*/ {
inline uint64_t lastIn() const { return _lastIn; } unsigned int p = startAt;
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)); }
private: private:
uint64_t _lastOut; uint64_t _lastSend;
uint64_t _lastIn; uint64_t _lastPing;
uint64_t _lastTrustEstablishedPacketReceived; uint64_t _lastKeepalive;
uint64_t _lastReceived;
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
AtomicCounter __refCount;
}; };
} // namespace ZeroTier } // namespace ZeroTier

View File

@@ -27,13 +27,9 @@
#include "Cluster.hpp" #include "Cluster.hpp"
#include "Packet.hpp" #include "Packet.hpp"
#ifndef AF_MAX #include <algorithm>
#if AF_INET > AF_INET6
#define AF_MAX AF_INET #define ZT_PEER_PATH_SORT_INTERVAL 5000
#else
#define AF_MAX AF_INET6
#endif
#endif
namespace ZeroTier { namespace ZeroTier {
@@ -41,20 +37,15 @@ namespace ZeroTier {
static uint32_t _natKeepaliveBuf = 0; 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),
_lastUsed(0),
_lastReceive(0), _lastReceive(0),
_lastUnicastFrame(0), _lastUnicastFrame(0),
_lastMulticastFrame(0), _lastMulticastFrame(0),
_lastAnnouncedTo(0),
_lastDirectPathPushSent(0), _lastDirectPathPushSent(0),
_lastDirectPathPushReceive(0), _lastDirectPathPushReceive(0),
_lastCredentialRequestSent(0), _lastPathSort(0),
_lastWhoisRequestReceived(0),
_lastEchoRequestReceived(0),
_lastComRequestReceived(0),
_lastComRequestSent(0),
_lastCredentialsReceived(0),
_lastTrustEstablishedPacketReceived(0),
RR(renv),
_remoteClusterOptimal4(0),
_vProto(0), _vProto(0),
_vMajor(0), _vMajor(0),
_vMinor(0), _vMinor(0),
@@ -63,31 +54,29 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
_numPaths(0), _numPaths(0),
_latency(0), _latency(0),
_directPathPushCutoffCount(0), _directPathPushCutoffCount(0),
_credentialsCutoffCount(0) _networkComs(4),
_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 SharedPtr<Path> &path, const InetAddress &localAddr,
const unsigned int hops, const InetAddress &remoteAddr,
const uint64_t packetId, unsigned int hops,
const Packet::Verb verb, uint64_t packetId,
const uint64_t inRePacketId, Packet::Verb verb,
const Packet::Verb inReVerb, uint64_t inRePacketId,
const bool trustEstablished) Packet::Verb inReVerb)
{ {
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(),path->address(),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(),remoteAddr,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);
@@ -105,7 +94,7 @@ void Peer::received(
} }
outp.append((uint16_t)redirectTo.port()); outp.append((uint16_t)redirectTo.port());
outp.armor(_key,true); outp.armor(_key,true);
path->send(RR,outp.data(),outp.size(),now); RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
} 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);
@@ -120,106 +109,161 @@ void Peer::received(
outp.append(redirectTo.rawIpData(),16); outp.append(redirectTo.rawIpData(),16);
} }
outp.armor(_key,true); outp.armor(_key,true);
path->send(RR,outp.data(),outp.size(),now); RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
} }
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)) if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME))
_lastUnicastFrame = now; _lastUnicastFrame = now;
else if (verb == Packet::VERB_MULTICAST_FRAME) else if (verb == Packet::VERB_MULTICAST_FRAME)
_lastMulticastFrame = now; _lastMulticastFrame = now;
if (trustEstablished) {
_lastTrustEstablishedPacketReceived = now;
path->trustedPacketReceived(now);
}
if (hops == 0) { if (hops == 0) {
bool pathIsConfirmed = false; bool pathIsConfirmed = false;
{ unsigned int np = _numPaths;
Mutex::Lock _l(_paths_m); for(unsigned int p=0;p<np;++p) {
for(unsigned int p=0;p<_numPaths;++p) { if ((_paths[p].address() == remoteAddr)&&(_paths[p].localAddress() == localAddr)) {
if (_paths[p].path->address() == path->address()) { _paths[p].received(now);
_paths[p].lastReceive = now;
_paths[p].path = path; // local address may have changed!
#ifdef ZT_ENABLE_CLUSTER #ifdef ZT_ENABLE_CLUSTER
_paths[p].localClusterSuboptimal = suboptimalPath; _paths[p].setClusterSuboptimal(suboptimalPath);
#endif #endif
pathIsConfirmed = true; pathIsConfirmed = true;
break; break;
} }
} }
}
if ( (!pathIsConfirmed) && (RR->node->shouldUsePathForZeroTierTraffic(path->localAddress(),path->address())) ) { if ((!pathIsConfirmed)&&(RR->node->shouldUsePathForZeroTierTraffic(localAddr,remoteAddr))) {
if (verb == Packet::VERB_OK) { if (verb == Packet::VERB_OK) {
Mutex::Lock _l(_paths_m);
// Since this is a new path, figure out where to put it (possibly replacing an old/dead one) Path *slot = (Path *)0;
unsigned int slot; if (np < ZT_MAX_PEER_NETWORK_PATHS) {
if (_numPaths < ZT_MAX_PEER_NETWORK_PATHS) { slot = &(_paths[np++]);
slot = _numPaths++;
} else { } else {
// First try to replace the worst within the same address family, if possible uint64_t slotWorstScore = 0xffffffffffffffffULL;
int worstSlot = -1; for(unsigned int p=0;p<ZT_MAX_PEER_NETWORK_PATHS;++p) {
uint64_t worstScore = 0xffffffffffffffffULL; if (!_paths[p].active(now)) {
for(unsigned int p=0;p<_numPaths;++p) { slot = &(_paths[p]);
if (_paths[p].path->address().ss_family == path->address().ss_family) { break;
const uint64_t s = _pathScore(p,now);
if (s < worstScore) {
worstScore = s;
worstSlot = (int)p;
}
}
}
if (worstSlot >= 0) {
slot = (unsigned int)worstSlot;
} else { } else {
// If we can't find one with the same family, replace the worst of any family const uint64_t score = _paths[p].score();
slot = ZT_MAX_PEER_NETWORK_PATHS - 1; if (score <= slotWorstScore) {
for(unsigned int p=0;p<_numPaths;++p) { slotWorstScore = score;
const uint64_t s = _pathScore(p,now); slot = &(_paths[p]);
if (s < worstScore) {
worstScore = s;
slot = p;
} }
} }
} }
} }
if (slot) {
_paths[slot].lastReceive = now; *slot = Path(localAddr,remoteAddr);
_paths[slot].path = path; slot->received(now);
#ifdef ZT_ENABLE_CLUSTER
slot->setClusterSuboptimal(suboptimalPath);
#endif
_numPaths = np;
}
#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());
attemptToContactAt(path->localAddress(),path->address(),now); TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),remoteAddr.toString().c_str());
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 (trustEstablished) { }
// 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
const bool haveCluster = (RR->cluster); if (RR->cluster)
#else return false;
const bool haveCluster = false;
#endif #endif
if ( ((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL) && (!haveCluster) ) {
_lastDirectPathPushSent = now; if (!force) {
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) {
@@ -230,9 +274,11 @@ void Peer::received(
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)
@@ -240,6 +286,7 @@ void Peer::received(
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());
@@ -275,179 +322,237 @@ void Peer::received(
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->send(RR,outp.data(),outp.size(),now); RR->node->putPacket(localAddr,toAddress,outp.data(),outp.size(),0);
}
}
}
}
} }
} }
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;
} }
return false;
}
bool Peer::sendDirect(const void *data,unsigned int len,uint64_t now,bool forceEvenIfDead) bool Peer::resetWithinScope(InetAddress::IpScope scope,uint64_t now)
{ {
Mutex::Lock _l(_paths_m); unsigned int np = _numPaths;
unsigned int x = 0;
int bestp = -1; unsigned int y = 0;
uint64_t best = 0ULL; while (x < np) {
for(unsigned int p=0;p<_numPaths;++p) { if (_paths[x].address().ipScope() == scope) {
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && (_paths[p].path->alive(now)||(forceEvenIfDead)) ) { // Resetting a path means sending a HELLO and then forgetting it. If we
const uint64_t s = _pathScore(p,now); // get OK(HELLO) then it will be re-learned.
if (s >= best) { sendHELLO(_paths[x].localAddress(),_paths[x].address(),now);
best = s;
bestp = (int)p;
}
}
}
if (bestp >= 0) {
return _paths[bestp].path->send(RR,data,len,now);
} else { } else {
return false; _paths[y++] = _paths[x];
}
}
SharedPtr<Path> Peer::getBestPath(uint64_t now,bool includeExpired)
{
Mutex::Lock _l(_paths_m);
int bestp = -1;
uint64_t best = 0ULL;
for(unsigned int p=0;p<_numPaths;++p) {
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) || (includeExpired) ) {
const uint64_t s = _pathScore(p,now);
if (s >= best) {
best = s;
bestp = (int)p;
}
}
}
if (bestp >= 0) {
return _paths[bestp].path;
} else {
return SharedPtr<Path>();
}
}
void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now)
{
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());
RR->node->expectReplyTo(outp.packetId());
outp.armor(_key,false); // HELLO is sent in the clear
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
}
void Peer::attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now)
{
if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
RR->node->expectReplyTo(outp.packetId());
outp.armor(_key,true);
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
} else {
sendHELLO(localAddr,atAddress,now);
}
}
bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
{
Mutex::Lock _l(_paths_m);
int bestp = -1;
uint64_t best = 0ULL;
for(unsigned int p=0;p<_numPaths;++p) {
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && ((inetAddressFamily < 0)||((int)_paths[p].path->address().ss_family == inetAddressFamily)) ) {
const uint64_t s = _pathScore(p,now);
if (s >= best) {
best = s;
bestp = (int)p;
}
}
}
if (bestp >= 0) {
if ((now - _paths[bestp].lastReceive) >= ZT_PEER_PING_PERIOD) {
attemptToContactAt(_paths[bestp].path->localAddress(),_paths[bestp].path->address(),now);
_paths[bestp].path->sent(now);
} else if (_paths[bestp].path->needsHeartbeat(now)) {
_natKeepaliveBuf += (uint32_t)((now * 0x9e3779b1) >> 1); // tumble this around to send constantly varying (meaningless) payloads
_paths[bestp].path->send(RR,&_natKeepaliveBuf,sizeof(_natKeepaliveBuf),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);
_paths[p].path->sent(now);
_paths[p].lastReceive = 0; // path will not be used unless it speaks again
} }
++x;
} }
_numPaths = y;
return (y < np);
} }
void Peer::getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const void Peer::getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const
{ {
Mutex::Lock _l(_paths_m); uint64_t bestV4 = 0,bestV6 = 0;
for(unsigned int p=0,np=_numPaths;p<np;++p) {
int bestp4 = -1,bestp6 = -1; if (_paths[p].active(now)) {
uint64_t best4 = 0ULL,best6 = 0ULL; uint64_t lr = _paths[p].lastReceived();
for(unsigned int p=0;p<_numPaths;++p) { if (lr) {
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && (_paths[p].path->alive(now)) ) { if (_paths[p].address().isV4()) {
if (_paths[p].path->address().ss_family == AF_INET) { if (lr >= bestV4) {
const uint64_t s = _pathScore(p,now); bestV4 = lr;
if (s >= best4) { v4 = _paths[p].address();
best4 = s; }
bestp4 = (int)p; } else if (_paths[p].address().isV6()) {
if (lr >= bestV6) {
bestV6 = lr;
v6 = _paths[p].address();
}
} }
} 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) bool Peer::networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfMembership &com) const
v4 = _paths[bestp4].path->address(); {
if (bestp6 >= 0) Mutex::Lock _l(_networkComs_m);
v6 = _paths[bestp6].path->address(); const _NetworkCom *ourCom = _networkComs.get(nwid);
if (ourCom)
return ourCom->com.agreesWith(com);
return false;
}
bool Peer::validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com)
{
// Sanity checks
if ((!com)||(com.issuedTo() != _id.address()))
return false;
// Return true if we already have this *exact* COM
{
Mutex::Lock _l(_networkComs_m);
_NetworkCom *ourCom = _networkComs.get(nwid);
if ((ourCom)&&(ourCom->com == com))
return true;
}
// 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
}
} else {
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
{
Mutex::Lock _l(_networkComs_m);
_networkComs.set(nwid,_NetworkCom(RR->node->now(),com));
}
return true;
}
bool Peer::needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime)
{
Mutex::Lock _l(_networkComs_m);
uint64_t &lastPushed = _lastPushedComs[nwid];
const uint64_t tmp = lastPushed;
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)
{
/* Dead path detection: if we have sent something to this peer and have not
* 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 (
(p.lastSend() > p.lastReceived()) &&
((p.lastSend() - p.lastReceived()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
((now - p.lastPing()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
(!p.isClusterSuboptimal()) &&
(!RR->topology->amRoot())
) {
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)) ) ) {
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
outp.armor(_key,true);
p.send(RR,outp.data(),outp.size(),now);
p.pinged(now);
} else {
sendHELLO(p.localAddress(),p.address(),now);
p.sent(now);
p.pinged(now);
}
p.increaseProbation();
}
}
Path *Peer::_getBestPath(const uint64_t now)
{
Path *bestPath = (Path *)0;
uint64_t bestPathScore = 0;
for(unsigned int i=0;i<_numPaths;++i) {
const uint64_t score = _paths[i].score();
if ((score >= bestPathScore)&&(_paths[i].active(now))) {
bestPathScore = score;
bestPath = &(_paths[i]);
}
}
if (bestPath)
_doDeadPathDetection(*bestPath,now);
return bestPath;
}
Path *Peer::_getBestPath(const uint64_t now,int inetAddressFamily)
{
Path *bestPath = (Path *)0;
uint64_t bestPathScore = 0;
for(unsigned int i=0;i<_numPaths;++i) {
const uint64_t score = _paths[i].score();
if (((int)_paths[i].address().ss_family == inetAddressFamily)&&(score >= bestPathScore)&&(_paths[i].active(now))) {
bestPathScore = score;
bestPath = &(_paths[i]);
}
}
if (bestPath)
_doDeadPathDetection(*bestPath,now);
return bestPath;
} }
} // namespace ZeroTier } // namespace ZeroTier

View File

@@ -31,6 +31,7 @@
#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"
@@ -43,6 +44,10 @@
#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 {
/** /**
@@ -68,6 +73,18 @@ 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())
*/ */
@@ -84,167 +101,149 @@ 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 path Path over which packet was received * @param RR Runtime environment
* @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 SharedPtr<Path> &path, const InetAddress &localAddr,
const unsigned int hops, const InetAddress &remoteAddr,
const uint64_t packetId, unsigned int hops,
const Packet::Verb verb, uint64_t packetId,
const uint64_t inRePacketId, Packet::Verb verb,
const Packet::Verb inReVerb, uint64_t inRePacketId = 0,
const bool trustEstablished); Packet::Verb inReVerb = Packet::VERB_NOP);
/**
* 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
*/ */
bool hasActivePathTo(uint64_t now,const InetAddress &addr) const; inline 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 which known path for an address family is optimal * Set all paths in the same ss_family that are not this one to cluster suboptimal
*
* Addresses in other families are not affected.
* *
* @param addr Address to make exclusive * @param addr Address to make exclusive
*/ */
inline void setClusterOptimal(const InetAddress &addr) inline void setClusterOptimalPathForAddressFamily(const InetAddress &addr)
{ {
if (addr.ss_family == AF_INET) { for(unsigned int p=0;p<_numPaths;++p) {
_remoteClusterOptimal4 = (uint32_t)reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_addr.s_addr; if (_paths[p].address().ss_family == addr.ss_family) {
} else if (addr.ss_family == AF_INET6) { _paths[p].setClusterSuboptimal(_paths[p].address() != addr);
memcpy(_remoteClusterOptimal6,reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_addr.s6_addr,16); }
} }
} }
/** /**
* Send via best direct path * Send via best 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
* @param forceEvenIfDead If true, send even if the path is not 'alive' * @return Path used on success or NULL on failure
* @return True if we actually sent something
*/ */
bool sendDirect(const void *data,unsigned int len,uint64_t now,bool forceEvenIfDead); inline Path *send(const void *data,unsigned int len,uint64_t now)
{
/** Path *const bestPath = getBestPath(now);
* Get the best current direct path if (bestPath) {
* if (bestPath->send(RR,data,len,now))
* @param now Current time return bestPath;
* @param includeDead If true, include even expired paths }
* @return Best current path or NULL if none return (Path *)0;
*/ }
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
* *
* No statistics or sent times are updated here. * This does not update any statistics. It's used to send initial HELLOs
* 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)
*/ */
void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now); void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl = 0);
/**
* 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
*/
void attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,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 -1 for any * @param inetAddressFamily Keep this address family alive, or 0 to simply pick current best ignoring family
* @return True if we have at least one direct path of the given family (or any if family is -1) * @return True if at least one direct path seems alive
*/ */
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
* @return True if this peer has at least one active and alive direct path * @param force If true, push regardless of rate limit
* @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 hasActiveDirectPath(uint64_t now) const; bool pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAddress,uint64_t now,bool force,bool includePrivatePaths);
/** /**
* Reset paths within a given IP scope and address family * @return All known direct paths to this peer (active or inactive)
*
* 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
*/ */
void resetWithinScope(InetAddress::IpScope scope,int inetAddressFamily,uint64_t now); inline std::vector<Path> paths() const
/**
* 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 getBestActiveAddresses(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< std::pair< SharedPtr<Path>,bool > > pp; std::vector<Path> 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(std::pair< SharedPtr<Path>,bool >(_paths[p].path,(now - _paths[p].lastReceive) > ZT_PEER_PATH_EXPIRATION)); pp.push_back(_paths[p]);
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 { return _lastReceive; } inline uint64_t lastReceive() const throw() { return _lastReceive; }
/**
* @return True if we've heard from this peer in less than ZT_PEER_ACTIVITY_TIMEOUT
*/
inline bool isAlive(const uint64_t now) const { return ((now - _lastReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
/** /**
* @return Time of most recent unicast frame received * @return Time of most recent unicast frame received
*/ */
inline uint64_t lastUnicastFrame() const { return _lastUnicastFrame; } inline uint64_t lastUnicastFrame() const throw() { return _lastUnicastFrame; }
/** /**
* @return Time of most recent multicast frame received * @return Time of most recent multicast frame received
*/ */
inline uint64_t lastMulticastFrame() const { return _lastMulticastFrame; } inline uint64_t lastMulticastFrame() const throw() { return _lastMulticastFrame; }
/** /**
* @return Time of most recent frame of any kind (unicast or multicast) * @return Time of most recent frame of any kind (unicast or multicast)
*/ */
inline uint64_t lastFrame() const { return std::max(_lastUnicastFrame,_lastMulticastFrame); } 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 isActive(uint64_t now) const { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); } inline uint64_t activelyTransferringFrames(uint64_t now) const throw() { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); }
/** /**
* @return Latency in milliseconds or 0 if unknown * @return Latency in milliseconds or 0 if unknown
@@ -270,7 +269,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_PING_PERIOD + 1000)) + 1)); return (l * (((unsigned int)tsr / (ZT_PEER_DIRECT_PING_DELAY + 1000)) + 1));
} }
/** /**
@@ -286,25 +285,47 @@ 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 hasLocalClusterOptimalPath(uint64_t now) const inline bool hasClusterOptimalPath(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].path->alive(now)) && (!_paths[p].localClusterSuboptimal) ) if ((_paths[p].active(now))&&(!_paths[p].isClusterSuboptimal()))
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 { return _key; } inline const unsigned char *key() const throw() { return _key; }
/** /**
* Set the currently known remote version of this peer's client * Set the currently known remote version of this peer's client
@@ -322,22 +343,69 @@ public:
_vRevision = (uint16_t)vrev; _vRevision = (uint16_t)vrev;
} }
inline unsigned int remoteVersionProtocol() const { return _vProto; } inline unsigned int remoteVersionProtocol() const throw() { return _vProto; }
inline unsigned int remoteVersionMajor() const { return _vMajor; } inline unsigned int remoteVersionMajor() const throw() { return _vMajor; }
inline unsigned int remoteVersionMinor() const { return _vMinor; } inline unsigned int remoteVersionMinor() const throw() { return _vMinor; }
inline unsigned int remoteVersionRevision() const { return _vRevision; } inline unsigned int remoteVersionRevision() const throw() { return _vRevision; }
inline bool remoteVersionKnown() const { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); } inline bool remoteVersionKnown() const throw() { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
/** /**
* @return True if peer has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms * 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
*/ */
inline bool trustEstablished(const uint64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); } void getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const;
/** /**
* Rate limit gate for VERB_PUSH_DIRECT_PATHS * Check network COM agreement with this peer
*
* @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
*/ */
inline bool rateGatePushDirectPaths(const uint64_t now) bool networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfMembership &com) const;
/**
* 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;
@@ -346,78 +414,6 @@ public:
return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT); return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
} }
/**
* Rate limit gate for VERB_NETWORK_CREDENTIALS
*/
inline bool rateGateCredentialsReceived(const uint64_t now)
{
if ((now - _lastCredentialsReceived) <= ZT_PEER_CREDENTIALS_CUTOFF_TIME)
++_credentialsCutoffCount;
else _credentialsCutoffCount = 0;
_lastCredentialsReceived = now;
return (_directPathPushCutoffCount < ZT_PEER_CREDEITIALS_CUTOFF_LIMIT);
}
/**
* Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE
*/
inline bool rateGateRequestCredentials(const uint64_t now)
{
if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastCredentialRequestSent = now;
return true;
}
return false;
}
/**
* Rate limit gate for inbound WHOIS requests
*/
inline bool rateGateInboundWhoisRequest(const uint64_t now)
{
if ((now - _lastWhoisRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastWhoisRequestReceived = now;
return true;
}
return false;
}
/**
* Rate limit gate for inbound ECHO requests
*/
inline bool rateGateEchoRequest(const uint64_t now)
{
if ((now - _lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastEchoRequestReceived = now;
return true;
}
return false;
}
/**
* 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;
}
/** /**
* Find a common set of addresses by which two peers can link, if any * Find a common set of addresses by which two peers can link, if any
* *
@@ -438,67 +434,168 @@ public:
else return std::pair<InetAddress,InetAddress>(); else return std::pair<InetAddress,InetAddress>();
} }
private: template<unsigned int C>
inline uint64_t _pathScore(const unsigned int p,const uint64_t now) const inline void serialize(Buffer<C> &b) const
{ {
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)); Mutex::Lock _l(_networkComs_m);
if (_paths[p].path->address().ss_family == AF_INET) { const unsigned int recSizePos = b.size();
s += (uint64_t)(ZT_PEER_PING_PERIOD * (unsigned long)(reinterpret_cast<const struct sockaddr_in *>(&(_paths[p].path->address()))->sin_addr.s_addr == _remoteClusterOptimal4)); b.addSize(4); // space for uint32_t field length
} else if (_paths[p].path->address().ss_family == AF_INET6) {
uint64_t clusterWeight = ZT_PEER_PING_PERIOD; b.append((uint16_t)1); // version of serialized Peer data
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) { _id.serialize(b,false);
if (a[i] != _remoteClusterOptimal6[i]) {
clusterWeight = 0; b.append((uint64_t)_lastUsed);
break; 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);
} }
} }
s += clusterWeight;
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);
}
} }
s += (ZT_PEER_PING_PERIOD / 2) * (uint64_t)_paths[p].path->alive(now); b.template setAt<uint32_t>(recSizePos,(uint32_t)(b.size() - (recSizePos + 4))); // set size
#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]; /**
uint8_t _remoteClusterOptimal6[16]; * Create a new Peer from a serialized instance
*
* @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>
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 ((p + recSize) > b.size())
return SharedPtr<Peer>(); // size invalid
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);
}
}
const unsigned int numNetworkComs = b.template at<uint32_t>(p); p += 4;
for(unsigned int i=0;i<numNetworkComs;++i) {
_NetworkCom &c = np->_networkComs[b.template at<uint64_t>(p)]; p += 8;
c.ts = b.template at<uint64_t>(p); p += 8;
p += c.com.deserialize(b,p);
}
const unsigned int numLastPushed = b.template at<uint32_t>(p); p += 4;
for(unsigned int i=0;i<numLastPushed;++i) {
const uint64_t nwid = b.template at<uint64_t>(p); p += 8;
const uint64_t ts = b.template at<uint64_t>(p); p += 8;
np->_lastPushedComs.set(nwid,ts);
}
return np;
}
private:
void _doDeadPathDetection(Path &p,const uint64_t now);
Path *_getBestPath(const uint64_t now);
Path *_getBestPath(const uint64_t now,int inetAddressFamily);
unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; // computed with key agreement, not serialized
const RuntimeEnvironment *RR;
uint64_t _lastUsed;
uint64_t _lastReceive; // direct or indirect uint64_t _lastReceive; // direct or indirect
uint64_t _lastUnicastFrame; uint64_t _lastUnicastFrame;
uint64_t _lastMulticastFrame; uint64_t _lastMulticastFrame;
uint64_t _lastAnnouncedTo;
uint64_t _lastDirectPathPushSent; uint64_t _lastDirectPathPushSent;
uint64_t _lastDirectPathPushReceive; uint64_t _lastDirectPathPushReceive;
uint64_t _lastCredentialRequestSent; uint64_t _lastPathSort;
uint64_t _lastWhoisRequestReceived;
uint64_t _lastEchoRequestReceived;
uint64_t _lastComRequestReceived;
uint64_t _lastComRequestSent;
uint64_t _lastCredentialsReceived;
uint64_t _lastTrustEstablishedPacketReceived;
const RuntimeEnvironment *RR;
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;
struct { Path _paths[ZT_MAX_PEER_NETWORK_PATHS];
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;
}; };

View File

@@ -35,6 +35,7 @@ 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
@@ -50,9 +51,11 @@ 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)
{ {
} }
@@ -79,9 +82,15 @@ 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

View File

@@ -33,29 +33,37 @@
#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 600000 #define ZT_SELFAWARENESS_ENTRY_TIMEOUT 3600000
namespace ZeroTier { namespace ZeroTier {
class _ResetWithinScope class _ResetWithinScope
{ {
public: public:
_ResetWithinScope(uint64_t now,int inetAddressFamily,InetAddress::IpScope scope) : _ResetWithinScope(uint64_t now,InetAddress::IpScope scope) :
_now(now), _now(now),
_family(inetAddressFamily),
_scope(scope) {} _scope(scope) {}
inline void operator()(Topology &t,const SharedPtr<Peer> &p) { p->resetWithinScope(_scope,_family,_now); } inline void operator()(Topology &t,const SharedPtr<Peer> &p)
{
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(128) _phy(32)
{
}
SelfAwareness::~SelfAwareness()
{ {
} }
@@ -71,11 +79,9 @@ 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;
entry.trusted = trusted; 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());
// 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.
@@ -90,14 +96,23 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &receivedOnLoc
} }
} }
// Reset all paths within this scope and address family // Reset all paths within this scope
_ResetWithinScope rset(now,myPhysicalAddress.ss_family,(InetAddress::IpScope)scope); _ResetWithinScope rset(now,(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;
} }
} }
@@ -118,70 +133,49 @@ 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
* *
* For each IP address reported by a trusted (upstream) peer, we find * In short: a great many symmetric NATs allocate ports sequentially.
* the external port most recently reported by ANY peer for that IP. * This is common on enterprise and carrier grade NATs as well as consumer
* * devices. This code generates a list of "you might try this" addresses by
* We only do any of this for global IPv4 addresses since private IPs * extrapolating likely port assignments from currently known external
* and IPv6 are not going to have symmetric NAT. * global IPv4 surfaces. These can then be included in a PUSH_DIRECT_PATHS
* * message to another peer, causing it to possibly try these addresses and
* SECURITY NOTE: * bust our local symmetric NAT. It works often enough to be worth the
* * 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. */
std::map< uint32_t,std::pair<uint64_t,unsigned int> > maxPortByIp; // Gather unique surfaces indexed by local received-on address and flag
InetAddress theOneTrueSurface; // us as behind a symmetric NAT if there is more than one.
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::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)); std::set<InetAddress> &s = surfaces[k->receivedOnLocalAddress];
if ((mp != maxPortByIp.end())&&(mp->second.first < e->ts)) { s.insert(e->mySurface);
mp->second.first = e->ts; symmetric = symmetric||(s.size() > 1);
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(unsigned int k=1;k<=3;++k) { for(std::map< InetAddress,std::set<InetAddress> >::iterator si(surfaces.begin());si!=surfaces.end();++si) {
for(std::map< uint32_t,std::pair<uint64_t,unsigned int> >::iterator i(maxPortByIp.begin());i!=maxPortByIp.end();++i) { for(std::set<InetAddress>::iterator i(si->second.begin());i!=si->second.end();++i) {
unsigned int p = i->second.second + k; InetAddress ipp(*i);
if (p > 65535) p -= 64511; unsigned int p = ipp.port() + 1 + ((unsigned int)RR->node->prng() & 3);
InetAddress pred(&(i->first),4,p); if (p >= 65535)
if (std::find(r.begin(),r.end(),pred) == r.end()) p -= 64510; // NATs seldom use ports <=1024 so wrap to 1025
r.push_back(pred); ipp.setPort(p);
if ((si->second.count(ipp) == 0)&&(std::find(r.begin(),r.end(),ipp) == r.end())) {
r.push_back(ipp);
}
} }
} }
return r; return r;

View File

@@ -36,6 +36,7 @@ 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
@@ -81,10 +82,9 @@ private:
{ {
InetAddress mySurface; InetAddress mySurface;
uint64_t ts; uint64_t ts;
bool trusted;
PhySurfaceEntry() : mySurface(),ts(0),trusted(false) {} PhySurfaceEntry() : mySurface(),ts(0) {}
PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t),trusted(false) {} PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t) {}
}; };
const RuntimeEnvironment *RR; const RuntimeEnvironment *RR;

View File

@@ -119,40 +119,16 @@ 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); }

View File

@@ -73,9 +73,6 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
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
@@ -93,11 +90,11 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
_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->send(RR,outp.data(),outp.size(),now); RR->node->putPacket(localAddr,fromAddr,outp.data(),outp.size());
} }
} }
} else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { // SECURITY: min length check is important since we do some C-style stuff below! } else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { // min length check is important!
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 ----------------------------------------------------
@@ -105,25 +102,14 @@ 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()) {
switch(RR->node->relayPolicy()) { // Fragment is not for us, so try to relay it
case ZT_RELAY_POLICY_ALWAYS:
break;
case ZT_RELAY_POLICY_TRUSTED:
if (!path->trustEstablished(now))
return;
break;
// case ZT_RELAY_POLICY_NEVER:
default:
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->sendDirect(fragment.data(),fragment.size(),now,false))) { if ((!relayTo)||(!relayTo->send(fragment.data(),fragment.size(),now))) {
#ifdef ZT_ENABLE_CLUSTER #ifdef ZT_ENABLE_CLUSTER
if (RR->cluster) { if (RR->cluster) {
RR->cluster->sendViaCluster(Address(),destination,fragment.data(),fragment.size(),false); RR->cluster->sendViaCluster(Address(),destination,fragment.data(),fragment.size(),false);
@@ -134,7 +120,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
// Don't know peer or no direct path -- so relay via root server // Don't know peer or no direct path -- so relay via root server
relayTo = RR->topology->getBestRoot(); relayTo = RR->topology->getBestRoot();
if (relayTo) if (relayTo)
relayTo->sendDirect(fragment.data(),fragment.size(),now,true); relayTo->send(fragment.data(),fragment.size(),now);
} }
} 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());
@@ -178,7 +164,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)) { if (rq->frag0.tryDecode(RR,false)) {
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
@@ -214,25 +200,14 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
//TRACE("<< %.16llx %s -> %s (size: %u)",(unsigned long long)packet->packetId(),source.toString().c_str(),destination.toString().c_str(),packet->size()); //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()) { if (destination != RR->identity.address()) {
switch(RR->node->relayPolicy()) {
case ZT_RELAY_POLICY_ALWAYS:
break;
case ZT_RELAY_POLICY_TRUSTED:
if (!path->trustEstablished(now))
return;
break;
// case ZT_RELAY_POLICY_NEVER:
default:
return;
}
Packet packet(data,len); Packet packet(data,len);
// Packet is not for us, so try to relay it
if (packet.hops() < ZT_RELAY_MAX_HOPS) { if (packet.hops() < ZT_RELAY_MAX_HOPS) {
packet.incrementHops(); packet.incrementHops();
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination); SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
if ((relayTo)&&((relayTo->sendDirect(packet.data(),packet.size(),now,false)))) { if ((relayTo)&&((relayTo->send(packet.data(),packet.size(),now)))) {
Mutex::Lock _l(_lastUniteAttempt_m); Mutex::Lock _l(_lastUniteAttempt_m);
uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)]; uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
if ((now - luts) >= ZT_MIN_UNITE_INTERVAL) { if ((now - luts) >= ZT_MIN_UNITE_INTERVAL) {
@@ -256,7 +231,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
#endif #endif
relayTo = RR->topology->getBestRoot(&source,1,true); relayTo = RR->topology->getBestRoot(&source,1,true);
if (relayTo) if (relayTo)
relayTo->sendDirect(packet.data(),packet.size(),now,true); relayTo->send(packet.data(),packet.size(),now);
} }
} else { } else {
TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet.source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str()); TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet.source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
@@ -273,7 +248,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,path,now); rq->frag0.init(data,len,localAddr,fromAddr,now);
rq->totalFragments = 0; rq->totalFragments = 0;
rq->haveFragments = 1; rq->haveFragments = 1;
rq->complete = false; rq->complete = false;
@@ -284,24 +259,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,path,now); rq->frag0.init(data,len,localAddr,fromAddr,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)) { if (rq->frag0.tryDecode(RR,false)) {
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,path,now); rq->frag0.init(data,len,localAddr,fromAddr,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,path,now); IncomingPacket packet(data,len,localAddr,fromAddr,now);
if (!packet.tryDecode(RR)) { if (!packet.tryDecode(RR,false)) {
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;
@@ -338,6 +313,12 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
if (to == network->mac()) if (to == network->mac())
return; 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 = false;
if (from != network->mac()) { if (from != network->mac()) {
@@ -349,7 +330,8 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
} }
if (to.isMulticast()) { if (to.isMulticast()) {
MulticastGroup multicastGroup(to,0); // Destination is a multicast address (including broadcast)
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)) ) {
@@ -362,63 +344,40 @@ 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. */
multicastGroup = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0)); mg = 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());
return; return;
} }
} else if ((etherType == ZT_ETHERTYPE_IPV6)&&(len >= (40 + 8 + 16))) { } else if ((etherType == ZT_ETHERTYPE_IPV6)&&(len >= (40 + 8 + 16))) {
// IPv6 NDP emulation for certain very special patterns of private IPv6 addresses -- if enabled /* IPv6 NDP emulation on ZeroTier-RFC4193 addressed networks! This allows
if ((network->config().ndpEmulation())&&(reinterpret_cast<const uint8_t *>(data)[6] == 0x3a)&&(reinterpret_cast<const uint8_t *>(data)[40] == 0x87)) { // ICMPv6 neighbor solicitation * for multicast-free operation in IPv6 networks, which both improves
Address v6EmbeddedAddress; * performance and is friendlier to mobile and (especially) IoT devices.
const uint8_t *const pkt6 = reinterpret_cast<const uint8_t *>(data) + 40 + 8; * In the future there may be a no-multicast build option for embedded
const uint8_t *my6 = (const uint8_t *)0; * and IoT use and this will be the preferred addressing mode. Note that
* it plays nice with our L2 emulation philosophy and even with bridging.
// ZT-RFC4193 address: fdNN:NNNN:NNNN:NNNN:NN99:93DD:DDDD:DDDD / 88 (one /128 per actual host) * While "real" devices behind the bridge can't have ZT-RFC4193 addresses
* themselves, they can look these addresses up with NDP and it will
// ZT-6PLANE address: fcXX:XXXX:XXDD:DDDD:DDDD:####:####:#### / 40 (one /80 per actual host) * work just fine. */
// (XX - lower 32 bits of network ID XORed with higher 32 bits) if ((reinterpret_cast<const uint8_t *>(data)[6] == 0x3a)&&(reinterpret_cast<const uint8_t *>(data)[40] == 0x87)) { // ICMPv6 neighbor solicitation
// For these to work, we must have a ZT-managed address assigned in one of the
// above formats, and the query must match its prefix.
for(unsigned int sipk=0;sipk<network->config().staticIpCount;++sipk) { for(unsigned int sipk=0;sipk<network->config().staticIpCount;++sipk) {
const InetAddress *const sip = &(network->config().staticIps[sipk]); const InetAddress *sip = &(network->config().staticIps[sipk]);
if (sip->ss_family == AF_INET6) { if ((sip->ss_family == AF_INET6)&&(Utils::ntoh((uint16_t)reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_port) == 88)) {
my6 = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_addr.s6_addr); const uint8_t *my6 = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_addr.s6_addr);
const unsigned int sipNetmaskBits = Utils::ntoh((uint16_t)reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_port); if ((my6[0] == 0xfd)&&(my6[9] == 0x99)&&(my6[10] == 0x93)) { // ZT-RFC4193 == fd__:____:____:____:__99:93__:____:____ / 88
if ((sipNetmaskBits == 88)&&(my6[0] == 0xfd)&&(my6[9] == 0x99)&&(my6[10] == 0x93)) { // ZT-RFC4193 /88 ??? const uint8_t *pkt6 = reinterpret_cast<const uint8_t *>(data) + 40 + 8;
unsigned int ptr = 0; unsigned int ptr = 0;
while (ptr != 11) { while (ptr != 11) {
if (pkt6[ptr] != my6[ptr]) if (pkt6[ptr] != my6[ptr])
break; break;
++ptr; ++ptr;
} }
if (ptr == 11) { // prefix match! if (ptr == 11) { // /88 matches an assigned address on this network
v6EmbeddedAddress.setTo(pkt6 + ptr,5); const Address atPeer(pkt6 + ptr,5);
break; if (atPeer != RR->identity.address()) {
} const MAC atPeerMac(atPeer,network->id());
} else if (sipNetmaskBits == 40) { // ZT-6PLANE /40 ??? TRACE("ZT-RFC4193 NDP emulation: %.16llx: forging response for %s/%s",network->id(),atPeer.toString().c_str(),atPeerMac.toString().c_str());
const uint32_t nwid32 = (uint32_t)((network->id() ^ (network->id() >> 32)) & 0xffffffff);
if ( (my6[0] == 0xfc) && (my6[1] == (uint8_t)((nwid32 >> 24) & 0xff)) && (my6[2] == (uint8_t)((nwid32 >> 16) & 0xff)) && (my6[3] == (uint8_t)((nwid32 >> 8) & 0xff)) && (my6[4] == (uint8_t)(nwid32 & 0xff))) {
unsigned int ptr = 0;
while (ptr != 5) {
if (pkt6[ptr] != my6[ptr])
break;
++ptr;
}
if (ptr == 5) { // prefix match!
v6EmbeddedAddress.setTo(pkt6 + ptr,5);
break;
}
}
}
}
}
if ((v6EmbeddedAddress)&&(v6EmbeddedAddress != RR->identity.address())) {
const MAC peerMac(v6EmbeddedAddress,network->id());
TRACE("IPv6 NDP emulation: %.16llx: forging response for %s/%s",network->id(),v6EmbeddedAddress.toString().c_str(),peerMac.toString().c_str());
uint8_t adv[72]; uint8_t adv[72];
adv[0] = 0x60; adv[1] = 0x00; adv[2] = 0x00; adv[3] = 0x00; adv[0] = 0x60; adv[1] = 0x00; adv[2] = 0x00; adv[3] = 0x00;
@@ -431,7 +390,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
adv[44] = 0x60; adv[45] = 0x00; adv[46] = 0x00; adv[47] = 0x00; adv[44] = 0x60; adv[45] = 0x00; adv[46] = 0x00; adv[47] = 0x00;
for(int i=0;i<16;++i) adv[48 + i] = pkt6[i]; for(int i=0;i<16;++i) adv[48 + i] = pkt6[i];
adv[64] = 0x02; adv[65] = 0x01; adv[64] = 0x02; adv[65] = 0x01;
adv[66] = peerMac[0]; adv[67] = peerMac[1]; adv[68] = peerMac[2]; adv[69] = peerMac[3]; adv[70] = peerMac[4]; adv[71] = peerMac[5]; adv[66] = atPeerMac[0]; adv[67] = atPeerMac[1]; adv[68] = atPeerMac[2]; adv[69] = atPeerMac[3]; adv[70] = atPeerMac[4]; adv[71] = atPeerMac[5];
uint16_t pseudo_[36]; uint16_t pseudo_[36];
uint8_t *const pseudo = reinterpret_cast<uint8_t *>(pseudo_); uint8_t *const pseudo = reinterpret_cast<uint8_t *>(pseudo_);
@@ -446,16 +405,14 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
adv[42] = (checksum >> 8) & 0xff; adv[42] = (checksum >> 8) & 0xff;
adv[43] = checksum & 0xff; adv[43] = checksum & 0xff;
RR->node->putFrame(network->id(),network->userPtr(),peerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72); RR->node->putFrame(network->id(),network->userPtr(),atPeerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72);
return; // NDP emulation done. We have forged a "fake" reply, so no need to send actual NDP query. return; // stop processing: we have handled this frame with a spoofed local reply so no need to send it anywhere
} // 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.
@@ -463,71 +420,63 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
* 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(multicastGroup,RR->node->now()); network->learnBridgedMulticastGroup(mg,RR->node->now());
//TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),multicastGroup.toString().c_str(),etherTypeName(etherType),len); //TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),mg.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(),
multicastGroup, mg,
(fromBridged) ? from : MAC(), (fromBridged) ? from : MAC(),
etherType, etherType,
data, data,
len); len);
} else if (to[0] == MAC::firstOctetForNetwork(network->id())) {
return;
}
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 (!network->filterOutgoingPacket(false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId)) { if ((fromBridged)||(includeCom)) {
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); send(outp,true,network->id());
} 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); send(outp,true,network->id());
} }
//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;
@@ -561,34 +510,37 @@ 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) {
if (network->filterOutgoingPacket(true,RR->identity.address(),bridges[b],from,to,(const uint8_t *)data,len,etherType,vlanId)) { SharedPtr<Peer> bridgePeer(RR->topology->getPeer(bridges[b]));
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());
outp.append((uint8_t)0x00); if ( (network->config().isPrivate()) && (network->config().com) && ((!bridgePeer)||(bridgePeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) ) {
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); send(outp,true,network->id());
} 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) void Switch::send(const Packet &packet,bool encrypt,uint64_t nwid)
{ {
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;
} }
if (!_trySend(packet,encrypt)) { //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,nwid)) {
Mutex::Lock _l(_txQueue_m); Mutex::Lock _l(_txQueue_m);
_txQueue.push_back(TXQueueEntry(packet.destination(),RR->node->now(),packet,encrypt)); _txQueue.push_back(TXQueueEntry(packet.destination(),RR->node->now(),packet,encrypt,nwid));
} }
} }
@@ -638,7 +590,7 @@ bool Switch::unite(const Address &p1,const Address &p2)
outp.append(cg.first.rawIpData(),4); outp.append(cg.first.rawIpData(),4);
} }
outp.armor(p1p->key(),true); outp.armor(p1p->key(),true);
p1p->sendDirect(outp.data(),outp.size(),now,true); p1p->send(outp.data(),outp.size(),now);
} else { } else {
// Tell p2 where to find p1. // Tell p2 where to find p1.
Packet outp(p2,RR->identity.address(),Packet::VERB_RENDEZVOUS); Packet outp(p2,RR->identity.address(),Packet::VERB_RENDEZVOUS);
@@ -653,7 +605,7 @@ bool Switch::unite(const Address &p1,const Address &p2)
outp.append(cg.second.rawIpData(),4); outp.append(cg.second.rawIpData(),4);
} }
outp.armor(p2p->key(),true); outp.armor(p2p->key(),true);
p2p->sendDirect(outp.data(),outp.size(),now,true); p2p->send(outp.data(),outp.size(),now);
} }
++alt; // counts up and also flips LSB ++alt; // counts up and also flips LSB
} }
@@ -661,6 +613,17 @@ bool Switch::unite(const Address &p1,const Address &p2)
return true; 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));
}
}
void Switch::requestWhois(const Address &addr) void Switch::requestWhois(const Address &addr)
{ {
bool inserted = false; bool inserted = false;
@@ -691,7 +654,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)) if (rq->frag0.tryDecode(RR,false))
rq->timestamp = 0; rq->timestamp = 0;
} }
} }
@@ -701,7 +664,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)) if (_trySend(txi->packet,txi->encrypt,txi->nwid))
_txQueue.erase(txi++); _txQueue.erase(txi++);
else ++txi; else ++txi;
} else ++txi; } else ++txi;
@@ -713,6 +676,42 @@ 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);
@@ -740,7 +739,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)) if (_trySend(txi->packet,txi->encrypt,txi->nwid))
_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());
@@ -765,41 +764,65 @@ unsigned long Switch::doTimerTasks(uint64_t now)
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> upstream(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false)); SharedPtr<Peer> root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
if (upstream) { if (root) {
Packet outp(upstream->address(),RR->identity.address(),Packet::VERB_WHOIS); Packet outp(root->address(),RR->identity.address(),Packet::VERB_WHOIS);
addr.appendTo(outp); addr.appendTo(outp);
RR->node->expectReplyTo(outp.packetId()); outp.armor(root->key(),true);
send(outp,true); if (root->send(outp.data(),outp.size(),RR->node->now()))
return root->address();
} }
return Address(); return Address();
} }
bool Switch::_trySend(const Packet &packet,bool encrypt) bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
{ {
const SharedPtr<Peer> peer(RR->topology->getPeer(packet.destination())); SharedPtr<Peer> peer(RR->topology->getPeer(packet.destination()));
if (peer) { if (peer) {
const uint64_t now = RR->node->now(); const uint64_t now = RR->node->now();
// First get the best path, and if it's dead (and this is not a root) SharedPtr<Network> network;
// we attempt to re-activate that path but this packet will flow if (nwid) {
// upstream. If the path comes back alive, it will be used in the future. network = RR->node->network(nwid);
// For roots we don't do the alive check since roots are not required if ((!network)||(!network->hasConfig()))
// to send heartbeats "down" and because we have to at least try to return false; // we probably just left this network, let its packets die
// go somewhere.
SharedPtr<Path> viaPath(peer->getBestPath(now,false));
if ( (viaPath) && (!viaPath->alive(now)) && (!RR->topology->isRoot(peer->identity())) ) {
if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) * 4,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL))
peer->attemptToContactAt(viaPath->localAddress(),viaPath->address(),now);
viaPath.zero();
} }
Path *viaPath = peer->getBestPath(now);
SharedPtr<Peer> relay;
if (!viaPath) { if (!viaPath) {
SharedPtr<Peer> relay(RR->topology->getBestRoot()); if (network) {
if ( (!relay) || (!(viaPath = relay->getBestPath(now,false))) ) { unsigned int bestq = ~((unsigned int)0); // max unsigned int since quality is lower==better
if (!(viaPath = peer->getBestPath(now,true))) unsigned int ptr = 0;
for(;;) {
const Address raddr(network->config().nextRelay(ptr));
if (raddr) {
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; 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);
} }
Packet tmp(packet); Packet tmp(packet);
@@ -807,12 +830,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
unsigned int chunkSize = std::min(tmp.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU); unsigned int chunkSize = std::min(tmp.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU);
tmp.setFragmented(chunkSize < tmp.size()); 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); tmp.armor(peer->key(),encrypt);
}
if (viaPath->send(RR,tmp.data(),chunkSize,now)) { if (viaPath->send(RR,tmp.data(),chunkSize,now)) {
if (chunkSize < tmp.size()) { if (chunkSize < tmp.size()) {
@@ -822,7 +840,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
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;
const unsigned int totalFragments = fragsRemaining + 1; 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));

View File

@@ -92,10 +92,15 @@ 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
* network traffic. Other traffic such as controller requests and regular
* protocol messages should specify zero.
*
* @param packet Packet to send * @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); void send(const Packet &packet,bool encrypt,uint64_t nwid);
/** /**
* Send RENDEZVOUS to two peers to permit them to directly connect * Send RENDEZVOUS to two peers to permit them to directly connect
@@ -108,6 +113,15 @@ public:
*/ */
bool unite(const Address &p1,const Address &p2); 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
* *
@@ -137,7 +151,7 @@ public:
private: private:
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); bool _trySend(const Packet &packet,bool encrypt,uint64_t nwid);
const RuntimeEnvironment *const RR; const RuntimeEnvironment *const RR;
uint64_t _lastBeaconResponse; uint64_t _lastBeaconResponse;
@@ -191,14 +205,16 @@ private:
struct TXQueueEntry struct TXQueueEntry
{ {
TXQueueEntry() {} TXQueueEntry() {}
TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc) : TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc,uint64_t nw) :
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;
}; };
@@ -225,6 +241,26 @@ 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

View File

@@ -44,10 +44,38 @@ static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x0
Topology::Topology(const RuntimeEnvironment *renv) : Topology::Topology(const RuntimeEnvironment *renv) :
RR(renv), RR(renv),
_trustedPathCount(0),
_amRoot(false) _amRoot(false)
{ {
// Get cached world if present 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 {
const unsigned int reclen = ( // each Peer serialized record is prefixed by a record length
((((unsigned int)all[ptr]) & 0xff) << 24) |
((((unsigned int)all[ptr + 1]) & 0xff) << 16) |
((((unsigned int)all[ptr + 2]) & 0xff) << 8) |
(((unsigned int)all[ptr + 3]) & 0xff)
);
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
}
}
delete deserializeBuf;
clean(RR->node->now());
std::string dsWorld(RR->node->dataStoreGet("world")); std::string dsWorld(RR->node->dataStoreGet("world"));
World cachedWorld; World cachedWorld;
if (dsWorld.length() > 0) { if (dsWorld.length() > 0) {
@@ -58,8 +86,6 @@ Topology::Topology(const RuntimeEnvironment *renv) :
cachedWorld = World(); // clear if cached world is invalid cachedWorld = World(); // clear if cached world is invalid
} }
} }
// Use default or cached world depending on which is shinier
World defaultWorld; 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);
@@ -74,6 +100,34 @@ Topology::Topology(const RuntimeEnvironment *renv) :
Topology::~Topology() 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;
}
} }
SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer) SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
@@ -96,6 +150,7 @@ SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
np = hp; np = hp;
} }
np->use(RR->node->now());
saveIdentity(np->identity()); saveIdentity(np->identity());
return np; return np;
@@ -112,6 +167,7 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
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;
} }
} }
@@ -125,6 +181,7 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
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;
} }
} }
@@ -138,9 +195,7 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
Identity Topology::getIdentity(const Address &zta) Identity Topology::getIdentity(const Address &zta)
{ {
if (zta == RR->identity.address()) { {
return RR->identity;
} else {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
const SharedPtr<Peer> *const ap = _peers.get(zta); const SharedPtr<Peer> *const ap = _peers.get(zta);
if (ap) if (ap)
@@ -173,9 +228,11 @@ SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCou
if (_rootAddresses[p] == RR->identity.address()) { if (_rootAddresses[p] == RR->identity.address()) {
for(unsigned long q=1;q<_rootAddresses.size();++q) { for(unsigned long q=1;q<_rootAddresses.size();++q) {
const SharedPtr<Peer> *const nextsn = _peers.get(_rootAddresses[(p + q) % _rootAddresses.size()]); const SharedPtr<Peer> *const nextsn = _peers.get(_rootAddresses[(p + q) % _rootAddresses.size()]);
if ((nextsn)&&((*nextsn)->hasActiveDirectPath(now))) if ((nextsn)&&((*nextsn)->hasActiveDirectPath(now))) {
(*nextsn)->use(now);
return *nextsn; return *nextsn;
} }
}
break; break;
} }
} }
@@ -209,8 +266,10 @@ SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCou
} }
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;
} }
@@ -221,7 +280,15 @@ SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCou
bool Topology::isUpstream(const Identity &id) const bool Topology::isUpstream(const Identity &id) const
{ {
return isRoot(id); if (isRoot(id))
return true;
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;
}
}
return false;
} }
bool Topology::worldUpdateIfValid(const World &newWorld) bool Topology::worldUpdateIfValid(const World &newWorld)
@@ -244,22 +311,14 @@ bool Topology::worldUpdateIfValid(const World &newWorld)
void Topology::clean(uint64_t now) void Topology::clean(uint64_t now)
{ {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
{
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 ( (!(*p)->isAlive(now)) && (std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end()) ) if (((now - (*p)->lastUsed()) >= ZT_PEER_IN_MEMORY_EXPIRATION)&&(std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end())) {
_peers.erase(*a); _peers.erase(*a);
} } else {
} (*p)->clean(now);
{
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);
} }
} }
} }

View File

@@ -28,12 +28,10 @@
#include <utility> #include <utility>
#include "Constants.hpp" #include "Constants.hpp"
#include "../include/ZeroTierOne.h"
#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"
@@ -90,22 +88,6 @@ public:
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(_lock);
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
* *
@@ -170,14 +152,6 @@ public:
return _rootAddresses; return _rootAddresses;
} }
/**
* @return Vector of active upstream addresses (including roots)
*/
inline std::vector<Address> upstreamAddresses() const
{
return rootAddresses();
}
/** /**
* @return Current World (copy) * @return Current World (copy)
*/ */
@@ -278,70 +252,14 @@ public:
*/ */
inline bool amRoot() const throw() { return _amRoot; } inline bool amRoot() const throw() { return _amRoot; }
/**
* Get the outbound trusted path ID for a physical address, or 0 if none
*
* @param physicalAddress Physical address to which we are sending the packet
* @return Trusted path ID or 0 if none (0 is not a valid trusted path ID)
*/
inline uint64_t getOutboundPathTrust(const InetAddress &physicalAddress)
{
for(unsigned int i=0;i<_trustedPathCount;++i) {
if (_trustedPathNetworks[i].containsAddress(physicalAddress))
return _trustedPathIds[i];
}
return 0;
}
/**
* Check whether in incoming trusted path marked packet is valid
*
* @param physicalAddress Originating physical address
* @param trustedPathId Trusted path ID from packet (from MAC field)
*/
inline bool shouldInboundPathBeTrusted(const InetAddress &physicalAddress,const uint64_t trustedPathId)
{
for(unsigned int i=0;i<_trustedPathCount;++i) {
if ((_trustedPathIds[i] == trustedPathId)&&(_trustedPathNetworks[i].containsAddress(physicalAddress)))
return true;
}
return false;
}
/**
* Set trusted paths in this topology
*
* @param networks Array of networks (prefix/netmask bits)
* @param ids Array of trusted path IDs
* @param count Number of trusted paths (if larger than ZT_MAX_TRUSTED_PATHS overflow is ignored)
*/
inline void setTrustedPaths(const InetAddress *networks,const uint64_t *ids,unsigned int count)
{
if (count > ZT_MAX_TRUSTED_PATHS)
count = ZT_MAX_TRUSTED_PATHS;
Mutex::Lock _l(_lock);
for(unsigned int i=0;i<count;++i) {
_trustedPathIds[i] = ids[i];
_trustedPathNetworks[i] = networks[i];
}
_trustedPathCount = count;
}
private: private:
Identity _getIdentity(const Address &zta); Identity _getIdentity(const Address &zta);
void _setWorld(const World &newWorld); void _setWorld(const World &newWorld);
const RuntimeEnvironment *const RR; const RuntimeEnvironment *const RR;
uint64_t _trustedPathIds[ZT_MAX_TRUSTED_PATHS];
InetAddress _trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
unsigned int _trustedPathCount;
World _world; World _world;
Hashtable< Address,SharedPtr<Peer> > _peers; Hashtable< Address,SharedPtr<Peer> > _peers;
Hashtable< Path::HashKey,SharedPtr<Path> > _paths;
std::vector< Address > _rootAddresses; std::vector< Address > _rootAddresses;
std::vector< SharedPtr<Peer> > _rootPeers; std::vector< SharedPtr<Peer> > _rootPeers;
bool _amRoot; bool _amRoot;

View File

@@ -262,24 +262,6 @@ std::vector<std::string> Utils::split(const char *s,const char *const sep,const
return fields; return fields;
} }
bool Utils::scopy(char *dest,unsigned int len,const char *src)
{
if (!len)
return false; // sanity check
if (!src) {
*dest = (char)0;
return true;
}
char *end = dest + len;
while ((*dest++ = *src++)) {
if (dest == end) {
*(--dest) = (char)0;
return false;
}
}
return true;
}
unsigned int Utils::snprintf(char *buf,unsigned int len,const char *fmt,...) unsigned int Utils::snprintf(char *buf,unsigned int len,const char *fmt,...)
throw(std::length_error) throw(std::length_error)
{ {
@@ -292,7 +274,6 @@ unsigned int Utils::snprintf(char *buf,unsigned int len,const char *fmt,...)
if ((n >= (int)len)||(n < 0)) { if ((n >= (int)len)||(n < 0)) {
if (len) if (len)
buf[len - 1] = (char)0; buf[len - 1] = (char)0;
abort();
throw std::length_error("buf[] overflow in Utils::snprintf"); throw std::length_error("buf[] overflow in Utils::snprintf");
} }

View File

@@ -49,6 +49,7 @@ public:
* @return True if strings are equal * @return True if strings are equal
*/ */
static inline bool secureEq(const void *a,const void *b,unsigned int len) static inline bool secureEq(const void *a,const void *b,unsigned int len)
throw()
{ {
uint8_t diff = 0; uint8_t diff = 0;
for(unsigned int i=0;i<len;++i) for(unsigned int i=0;i<len;++i)
@@ -224,17 +225,27 @@ public:
} }
/** /**
* Perform a safe C string copy, ALWAYS null-terminating the result * Perform a safe C string copy
* *
* This will never ever EVER result in dest[] not being null-terminated * @param dest Destination buffer
* regardless of any input parameter (other than len==0 which is invalid). * @param len Length of buffer
* * @param src Source string
* @param dest Destination buffer (must not be NULL)
* @param len Length of dest[] (if zero, false is returned and nothing happens)
* @param src Source string (if NULL, dest will receive a zero-length string and true is returned)
* @return True on success, false on overflow (buffer will still be 0-terminated) * @return True on success, false on overflow (buffer will still be 0-terminated)
*/ */
static bool scopy(char *dest,unsigned int len,const char *src); static inline bool scopy(char *dest,unsigned int len,const char *src)
throw()
{
if (!len)
return false; // sanity check
char *end = dest + len;
while ((*dest++ = *src++)) {
if (dest == end) {
*(--dest) = (char)0;
return false;
}
}
return true;
}
/** /**
* Variant of snprintf that is portable and throws an exception * Variant of snprintf that is portable and throws an exception
@@ -258,6 +269,7 @@ public:
* @return Number of bits set in this integer (0-32) * @return Number of bits set in this integer (0-32)
*/ */
static inline uint32_t countBits(uint32_t v) static inline uint32_t countBits(uint32_t v)
throw()
{ {
v = v - ((v >> 1) & (uint32_t)0x55555555); v = v - ((v >> 1) & (uint32_t)0x55555555);
v = (v & (uint32_t)0x33333333) + ((v >> 2) & (uint32_t)0x33333333); v = (v & (uint32_t)0x33333333) + ((v >> 2) & (uint32_t)0x33333333);
@@ -272,6 +284,7 @@ public:
* @return True if memory is all zero * @return True if memory is all zero
*/ */
static inline bool isZero(const void *p,unsigned int len) static inline bool isZero(const void *p,unsigned int len)
throw()
{ {
for(unsigned int i=0;i<len;++i) { for(unsigned int i=0;i<len;++i) {
if (((const unsigned char *)p)[i]) if (((const unsigned char *)p)[i])

View File

@@ -150,7 +150,7 @@ public:
if (fullSignatureCheck) { 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(_updatesMustBeSignedBy,tmp.data(),tmp.size(),update._signature); return C25519::verify(_updateSigningKey,tmp.data(),tmp.size(),update._signature);
} else return true; } else return true;
} }
return false; return false;
@@ -164,12 +164,12 @@ public:
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) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL); if (forSign)
b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
b.append((uint8_t)0x01); b.append((uint8_t)0x01); // version -- only one valid value for now
b.append((uint64_t)_id); b.append((uint64_t)_id);
b.append((uint64_t)_ts); b.append((uint64_t)_ts);
b.append(_updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN); b.append(_updateSigningKey.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 +179,8 @@ 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 (forSign) b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL); b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
} }
template<unsigned int C> template<unsigned int C>
@@ -191,11 +191,11 @@ public:
_roots.clear(); _roots.clear();
if (b[p++] != 0x01) if (b[p++] != 0x01)
throw std::invalid_argument("invalid object type"); throw std::invalid_argument("invalid World serialized version");
_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(_updatesMustBeSignedBy.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN; memcpy(_updateSigningKey.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++]; unsigned int numRoots = b[p++];
if (numRoots > ZT_WORLD_MAX_ROOTS) if (numRoots > ZT_WORLD_MAX_ROOTS)
@@ -216,13 +216,13 @@ public:
return (p - startAt); return (p - startAt);
} }
inline bool operator==(const World &w) const throw() { return ((_id == w._id)&&(_ts == w._ts)&&(_updatesMustBeSignedBy == w._updatesMustBeSignedBy)&&(_signature == w._signature)&&(_roots == w._roots)); } 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 throw() { return (!(*this == w)); } inline bool operator!=(const World &w) const throw() { return (!(*this == w)); }
protected: protected:
uint64_t _id; uint64_t _id;
uint64_t _ts; uint64_t _ts;
C25519::Public _updatesMustBeSignedBy; C25519::Public _updateSigningKey;
C25519::Signature _signature; C25519::Signature _signature;
std::vector<Root> _roots; std::vector<Root> _roots;
}; };

View File

@@ -1,13 +1,15 @@
OBJS=\ OBJS=\
controller/EmbeddedNetworkController.o \ ext/lz4/lz4.o \
ext/json-parser/json.o \
ext/http-parser/http_parser.o \
node/C25519.o \ node/C25519.o \
node/Capability.o \
node/CertificateOfMembership.o \ node/CertificateOfMembership.o \
node/Cluster.o \ node/Cluster.o \
node/DeferredPackets.o \
node/Dictionary.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 \
@@ -17,16 +19,13 @@ 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/BackgroundResolver.o \
osdep/ManagedRoute.o \
osdep/Http.o \ osdep/Http.o \
osdep/OSUtils.o \ osdep/OSUtils.o \
service/ClusterGeoIpService.o \ service/ClusterGeoIpService.o \

View File

@@ -48,12 +48,12 @@
#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"
#include "ext/json-parser/json.h"
#include "node/Identity.hpp" #include "node/Identity.hpp"
#include "node/CertificateOfMembership.hpp" #include "node/CertificateOfMembership.hpp"
#include "node/Utils.hpp" #include "node/Utils.hpp"
@@ -64,8 +64,6 @@
#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;
@@ -84,8 +82,6 @@ static OneService *volatile zt1Service = (OneService *)0;
/* zerotier-cli personality */ /* zerotier-cli personality */
/****************************************************************************/ /****************************************************************************/
// This is getting deprecated soon in favor of the stuff in cli/
static void cliPrintHelp(const char *pn,FILE *out) static void cliPrintHelp(const char *pn,FILE *out)
{ {
fprintf(out, fprintf(out,
@@ -103,13 +99,13 @@ static void cliPrintHelp(const char *pn,FILE *out)
fprintf(out," -D<path> - ZeroTier home path for parameter auto-detect"ZT_EOL_S); fprintf(out," -D<path> - ZeroTier home path for parameter auto-detect"ZT_EOL_S);
fprintf(out," -p<port> - HTTP port (default: auto)"ZT_EOL_S); fprintf(out," -p<port> - HTTP port (default: auto)"ZT_EOL_S);
fprintf(out," -T<token> - Authentication token (default: auto)"ZT_EOL_S); fprintf(out," -T<token> - Authentication token (default: auto)"ZT_EOL_S);
//fprintf(out," -H<ip> - HTTP server bind address (default: 127.0.0.1)"ZT_EOL_S);
fprintf(out,ZT_EOL_S"Available commands:"ZT_EOL_S); fprintf(out,ZT_EOL_S"Available commands:"ZT_EOL_S);
fprintf(out," info - Display status info"ZT_EOL_S); fprintf(out," info - Display status info"ZT_EOL_S);
fprintf(out," listpeers - List all peers"ZT_EOL_S); fprintf(out," listpeers - List all peers"ZT_EOL_S);
fprintf(out," listnetworks - List all networks"ZT_EOL_S); fprintf(out," listnetworks - List all networks"ZT_EOL_S);
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);
} }
static std::string cliFixJsonCRs(const std::string &s) static std::string cliFixJsonCRs(const std::string &s)
@@ -130,7 +126,10 @@ static int cli(int argc,char **argv)
#endif #endif
{ {
unsigned int port = 0; unsigned int port = 0;
std::string homeDir,command,arg1,arg2,authToken; std::string homeDir;
std::string command;
std::string arg1;
std::string authToken;
std::string ip("127.0.0.1"); std::string ip("127.0.0.1");
bool json = false; bool json = false;
for(int i=1;i<argc;++i) { for(int i=1;i<argc;++i) {
@@ -202,9 +201,7 @@ static int cli(int argc,char **argv)
return 0; return 0;
} }
} else { } else {
if (arg1.length()) if (command.length())
arg2 = argv[i];
else if (command.length())
arg1 = argv[i]; arg1 = argv[i];
else command = argv[i]; else command = argv[i];
} }
@@ -281,137 +278,221 @@ static int cli(int argc,char **argv)
return 1; return 1;
} }
} else if ((command == "info")||(command == "status")) { } else if ((command == "info")||(command == "status")) {
const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/status",requestHeaders,responseHeaders,responseBody); unsigned int scode = Http::GET(
1024 * 1024 * 16,
nlohmann::json j; 60000,
try { (const struct sockaddr *)&addr,
j = nlohmann::json::parse(responseBody); "/status",
} catch (std::exception &exc) { requestHeaders,
printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what()); responseHeaders,
return 1; responseBody);
} catch ( ... ) {
printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
return 1;
}
if (scode == 200) { if (scode == 200) {
std::ostringstream out;
if (json) { if (json) {
out << j.dump(2) << ZT_EOL_S; printf("%s",cliFixJsonCRs(responseBody).c_str());
} else {
if (j.is_object())
out << "200 info " << j["address"].get<std::string>() << " " << j["version"].get<std::string>() << " " << ((j["tcpFallbackActive"]) ? "TUNNELED" : ((j["online"]) ? "ONLINE" : "OFFLINE")) << ZT_EOL_S;
}
printf("%s",out.str().c_str());
return 0; 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;
}
}
} 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") {
const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/peer",requestHeaders,responseHeaders,responseBody); unsigned int scode = Http::GET(
1024 * 1024 * 16,
nlohmann::json j; 60000,
try { (const struct sockaddr *)&addr,
j = nlohmann::json::parse(responseBody); "/peer",
} catch (std::exception &exc) { requestHeaders,
printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what()); responseHeaders,
return 1; responseBody);
} catch ( ... ) {
printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
return 1;
}
if (scode == 200) { if (scode == 200) {
std::ostringstream out;
if (json) { if (json) {
out << j.dump(2) << ZT_EOL_S; printf("%s",cliFixJsonCRs(responseBody).c_str());
} else {
out << "200 listpeers <ztaddr> <path> <latency> <version> <role>" << ZT_EOL_S;
if (j.is_array()) {
for(unsigned long k=0;k<j.size();++k) {
auto &p = j[k];
std::string bestPath;
auto paths = p["paths"];
if (paths.is_array()) {
for(unsigned long i=0;i<paths.size();++i) {
auto &path = paths[i];
if (path["preferred"]) {
char tmp[256];
std::string addr = path["address"];
const uint64_t now = OSUtils::now();
Utils::snprintf(tmp,sizeof(tmp),"%s;%llu;%llu",addr.c_str(),now - (uint64_t)path["lastSend"],now - (uint64_t)path["lastReceive"]);
bestPath = tmp;
break;
}
}
}
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;
}
out << "200 listpeers " << p["address"].get<std::string>() << " " << bestPath << " " << p["latency"] << " " << ver << " " << p["role"].get<std::string>() << ZT_EOL_S;
}
}
}
printf("%s",out.str().c_str());
return 0; return 0;
} else {
printf("200 listpeers <ztaddr> <paths> <latency> <version> <role>"ZT_EOL_S);
json_value *j = json_parse(responseBody.c_str(),responseBody.length());
if (j) {
if (j->type == json_array) {
for(unsigned int p=0;p<j->u.array.length;++p) {
json_value *jp = j->u.array.values[p];
if (jp->type == json_object) {
const char *address = (const char *)0;
std::string paths;
int64_t latency = 0;
int64_t versionMajor = -1,versionMinor = -1,versionRev = -1;
const char *role = (const char *)0;
for(unsigned int k=0;k<jp->u.object.length;++k) {
if ((!strcmp(jp->u.object.values[k].name,"address"))&&(jp->u.object.values[k].value->type == json_string))
address = jp->u.object.values[k].value->u.string.ptr;
else if ((!strcmp(jp->u.object.values[k].name,"versionMajor"))&&(jp->u.object.values[k].value->type == json_integer))
versionMajor = jp->u.object.values[k].value->u.integer;
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 ((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;
}
} 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") {
const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/network",requestHeaders,responseHeaders,responseBody); unsigned int scode = Http::GET(
1024 * 1024 * 16,
nlohmann::json j; 60000,
try { (const struct sockaddr *)&addr,
j = nlohmann::json::parse(responseBody); "/network",
} catch (std::exception &exc) { requestHeaders,
printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what()); responseHeaders,
return 1; responseBody);
} catch ( ... ) {
printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
return 1;
}
if (scode == 200) { if (scode == 200) {
std::ostringstream out;
if (json) { if (json) {
out << j.dump(2) << ZT_EOL_S; printf("%s",cliFixJsonCRs(responseBody).c_str());
} else {
out << "200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>" << ZT_EOL_S;
if (j.is_array()) {
for(unsigned long i=0;i<j.size();++i) {
auto &n = j[i];
if (n.is_object()) {
std::string aa;
auto &assignedAddresses = n["assignedAddresses"];
if (assignedAddresses.is_array()) {
for(unsigned long j=0;j<assignedAddresses.size();++j) {
auto &addr = assignedAddresses[j];
if (addr.is_string()) {
if (aa.length() > 0) aa.push_back(',');
aa.append(addr);
}
}
}
if (aa.length() == 0) aa = "-";
std::string name = n["name"];
if (name.length() == 0) name = "-";
out << "200 listnetworks " << n["nwid"].get<std::string>() << " " << name << " " << n["mac"].get<std::string>() << " " << n["status"].get<std::string>() << " " << n["type"].get<std::string>() << " " << n["portDeviceName"].get<std::string>() << " " << aa << ZT_EOL_S;
}
}
}
}
printf("%s",out.str().c_str());
return 0; return 0;
} else {
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) {
if (j->type == json_array) {
for(unsigned int p=0;p<j->u.array.length;++p) {
json_value *jn = j->u.array.values[p];
if (jn->type == json_object) {
const char *nwid = (const char *)0;
const char *name = "";
const char *mac = (const char *)0;
const char *status = (const char *)0;
const char *type = (const char *)0;
const char *portDeviceName = "";
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 ((nwid)&&(mac)&&(status)&&(type)) {
printf("200 listnetworks %s %s %s %s %s %s %s"ZT_EOL_S,
nwid,
(((name)&&(name[0])) ? name : "-"),
mac,
status,
type,
(((portDeviceName)&&(portDeviceName[0])) ? portDeviceName : "-"),
((ips.length() > 0) ? ips.c_str() : "-"));
}
}
}
}
json_value_free(j);
}
}
} 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;
@@ -468,44 +549,6 @@ 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 == "set") {
if (arg1.length() != 16) {
cliPrintHelp(argv[0],stderr);
return 2;
}
std::size_t eqidx = arg2.find('=');
if (eqidx != std::string::npos) {
if ((arg2.substr(0,eqidx) == "allowManaged")||(arg2.substr(0,eqidx) == "allowGlobal")||(arg2.substr(0,eqidx) == "allowDefault")) {
char jsons[1024];
Utils::snprintf(jsons,sizeof(jsons),"{\"%s\":%s}",
arg2.substr(0,eqidx).c_str(),
(((arg2.substr(eqidx,2) == "=t")||(arg2.substr(eqidx,2) == "=1")) ? "true" : "false"));
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("/network/") + arg1).c_str(),
requestHeaders,
jsons,
strlen(jsons),
responseHeaders,
responseBody);
if (scode == 200) {
printf("%s",cliFixJsonCRs(responseBody).c_str());
return 0;
} else {
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
return 1;
}
}
} else {
cliPrintHelp(argv[0],stderr);
return 2;
}
} else { } else {
cliPrintHelp(argv[0],stderr); cliPrintHelp(argv[0],stderr);
return 0; return 0;
@@ -1086,12 +1129,6 @@ 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)) {

View File

@@ -34,6 +34,7 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>

View File

@@ -37,10 +37,9 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#if !defined(__ANDROID__)
#include <ifaddrs.h> #include <ifaddrs.h>
#ifdef __LINUX__
#include <sys/ioctl.h>
#include <net/if.h>
#endif #endif
#endif #endif
@@ -48,7 +47,6 @@
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include <map>
#include "../node/NonCopyable.hpp" #include "../node/NonCopyable.hpp"
#include "../node/InetAddress.hpp" #include "../node/InetAddress.hpp"
@@ -129,9 +127,14 @@ public:
template<typename PHY_HANDLER_TYPE,typename INTERFACE_CHECKER> template<typename PHY_HANDLER_TYPE,typename INTERFACE_CHECKER>
void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int port,INTERFACE_CHECKER &ifChecker) void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int port,INTERFACE_CHECKER &ifChecker)
{ {
std::map<InetAddress,std::string> localIfAddrs; std::vector<InetAddress> localIfAddrs;
std::vector<_Binding> newBindings;
std::vector<std::string>::const_iterator si;
std::vector<InetAddress>::const_iterator ii;
typename std::vector<_Binding>::const_iterator bi;
PhySocket *udps; PhySocket *udps;
//PhySocket *tcps; //PhySocket *tcps;
InetAddress ip;
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
#ifdef __WINDOWS__ #ifdef __WINDOWS__
@@ -149,10 +152,11 @@ public:
default: break; default: break;
case InetAddress::IP_SCOPE_PSEUDOPRIVATE: case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
case InetAddress::IP_SCOPE_GLOBAL: case InetAddress::IP_SCOPE_GLOBAL:
//case InetAddress::IP_SCOPE_LINK_LOCAL:
case InetAddress::IP_SCOPE_SHARED: case InetAddress::IP_SCOPE_SHARED:
case InetAddress::IP_SCOPE_PRIVATE: case InetAddress::IP_SCOPE_PRIVATE:
ip.setPort(port); ip.setPort(port);
localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string())); localIfAddrs.push_back(ip);
break; break;
} }
} }
@@ -163,6 +167,7 @@ public:
} }
#else // not __WINDOWS__ #else // not __WINDOWS__
#if !defined(__ANDROID__)
struct ifaddrs *ifatbl = (struct ifaddrs *)0; struct ifaddrs *ifatbl = (struct ifaddrs *)0;
struct ifaddrs *ifa; struct ifaddrs *ifa;
@@ -170,16 +175,17 @@ public:
ifa = ifatbl; ifa = ifatbl;
while (ifa) { while (ifa) {
if ((ifa->ifa_name)&&(ifa->ifa_addr)) { if ((ifa->ifa_name)&&(ifa->ifa_addr)) {
InetAddress ip = *(ifa->ifa_addr); ip = *(ifa->ifa_addr);
if (ifChecker.shouldBindInterface(ifa->ifa_name,ip)) { if (ifChecker.shouldBindInterface(ifa->ifa_name,ip)) {
switch(ip.ipScope()) { switch(ip.ipScope()) {
default: break; default: break;
case InetAddress::IP_SCOPE_PSEUDOPRIVATE: case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
case InetAddress::IP_SCOPE_GLOBAL: case InetAddress::IP_SCOPE_GLOBAL:
//case InetAddress::IP_SCOPE_LINK_LOCAL:
case InetAddress::IP_SCOPE_SHARED: case InetAddress::IP_SCOPE_SHARED:
case InetAddress::IP_SCOPE_PRIVATE: case InetAddress::IP_SCOPE_PRIVATE:
ip.setPort(port); ip.setPort(port);
localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name))); localIfAddrs.push_back(ip);
break; break;
} }
} }
@@ -188,56 +194,43 @@ public:
} }
freeifaddrs(ifatbl); freeifaddrs(ifatbl);
} }
#endif
#endif #endif
// Default to binding to wildcard if we can't enumerate addresses // Default to binding to wildcard if we can't enumerate addresses
if (localIfAddrs.empty()) { if (localIfAddrs.size() == 0) {
localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((uint32_t)0,port),std::string())); localIfAddrs.push_back(InetAddress((uint32_t)0,port));
localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,port),std::string())); localIfAddrs.push_back(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,port));
} }
// Close any old bindings to anything that doesn't exist anymore // Close any old bindings to anything that doesn't exist anymore
for(typename std::vector<_Binding>::const_iterator bi(_bindings.begin());bi!=_bindings.end();++bi) { for(bi=_bindings.begin();bi!=_bindings.end();++bi) {
if (localIfAddrs.find(bi->address) == localIfAddrs.end()) { if (std::find(localIfAddrs.begin(),localIfAddrs.end(),bi->address) == localIfAddrs.end()) {
phy.close(bi->udpSock,false); phy.close(bi->udpSock,false);
phy.close(bi->tcpListenSock,false); phy.close(bi->tcpListenSock,false);
} }
} }
std::vector<_Binding> newBindings; for(ii=localIfAddrs.begin();ii!=localIfAddrs.end();++ii) {
for(std::map<InetAddress,std::string>::const_iterator ii(localIfAddrs.begin());ii!=localIfAddrs.end();++ii) { // Copy over bindings that still are valid
typename std::vector<_Binding>::const_iterator bi(_bindings.begin()); for(bi=_bindings.begin();bi!=_bindings.end();++bi) {
while (bi != _bindings.end()) { if (bi->address == *ii) {
if (bi->address == ii->first) {
newBindings.push_back(*bi); newBindings.push_back(*bi);
break; break;
} }
++bi;
} }
// Add new bindings
if (bi == _bindings.end()) { if (bi == _bindings.end()) {
udps = phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0,ZT_UDP_DESIRED_BUF_SIZE); udps = phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(*ii)),(void *)0,ZT_UDP_DESIRED_BUF_SIZE);
if (udps) { if (udps) {
//tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&ii),(void *)0); //tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&ii),(void *)0);
//if (tcps) { //if (tcps) {
#ifdef __LINUX__
// Bind Linux sockets to their device so routes tha we manage do not override physical routes (wish all platforms had this!)
if (ii->second.length() > 0) {
int fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(udps);
char tmp[256];
Utils::scopy(tmp,sizeof(tmp),ii->second.c_str());
if (fd >= 0) {
if (setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)) != 0) {
fprintf(stderr,"WARNING: unable to set SO_BINDTODEVICE to bind %s to %s\n",ii->first.toIpString().c_str(),ii->second.c_str());
}
}
}
#endif // __LINUX__
newBindings.push_back(_Binding()); newBindings.push_back(_Binding());
newBindings.back().udpSock = udps; newBindings.back().udpSock = udps;
//newBindings.back().tcpListenSock = tcps; //newBindings.back().tcpListenSock = tcps;
newBindings.back().address = ii->first; newBindings.back().address = *ii;
//} else { //} else {
// phy.close(udps,false); // phy.close(udps,false);
//} //}
@@ -245,6 +238,12 @@ public:
} }
} }
/*
for(bi=newBindings.begin();bi!=newBindings.end();++bi) {
printf("Binder: bound to %s\n",bi->address.toString().c_str());
}
*/
// Swapping pointers and then letting the old one fall out of scope is faster than copying again // Swapping pointers and then letting the old one fall out of scope is faster than copying again
_bindings.swap(newBindings); _bindings.swap(newBindings);
} }

View File

@@ -25,12 +25,7 @@
#include "OSUtils.hpp" #include "OSUtils.hpp"
#include "../node/Constants.hpp" #include "../node/Constants.hpp"
#include "../node/Utils.hpp" #include "../node/Utils.hpp"
#ifdef ZT_USE_SYSTEM_HTTP_PARSER
#include <http_parser.h>
#else
#include "../ext/http-parser/http_parser.h" #include "../ext/http-parser/http_parser.h"
#endif
namespace ZeroTier { namespace ZeroTier {
@@ -38,18 +33,12 @@ namespace {
static int ShttpOnMessageBegin(http_parser *parser); static int ShttpOnMessageBegin(http_parser *parser);
static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length); static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length);
#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2)
static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length); static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length);
#else
static int ShttpOnStatus(http_parser *parser);
#endif
static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length); static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length);
static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length); static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length);
static int ShttpOnHeadersComplete(http_parser *parser); static int ShttpOnHeadersComplete(http_parser *parser);
static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length); static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length);
static int ShttpOnMessageComplete(http_parser *parser); static int ShttpOnMessageComplete(http_parser *parser);
#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 1)
static const struct http_parser_settings HTTP_PARSER_SETTINGS = { static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
ShttpOnMessageBegin, ShttpOnMessageBegin,
ShttpOnUrl, ShttpOnUrl,
@@ -60,17 +49,6 @@ static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
ShttpOnBody, ShttpOnBody,
ShttpOnMessageComplete ShttpOnMessageComplete
}; };
#else
static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
ShttpOnMessageBegin,
ShttpOnUrl,
ShttpOnHeaderField,
ShttpOnValue,
ShttpOnHeadersComplete,
ShttpOnBody,
ShttpOnMessageComplete
};
#endif
struct HttpPhyHandler struct HttpPhyHandler
{ {
@@ -102,7 +80,7 @@ struct HttpPhyHandler
phy->close(sock); phy->close(sock);
} }
inline void phyOnTcpWritable(PhySocket *sock,void **uptr) inline void phyOnTcpWritable(PhySocket *sock,void **uptr, bool lwip_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);
@@ -148,18 +126,12 @@ static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length)
{ {
return 0; return 0;
} }
#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2)
static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length) static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length)
#else
static int ShttpOnStatus(http_parser *parser)
#endif
{ {
/*
HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data); HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
hh->messageSize += (unsigned long)length; hh->messageSize += (unsigned long)length;
if (hh->messageSize > hh->maxResponseSize) if (hh->messageSize > hh->maxResponseSize)
return -1; return -1;
*/
return 0; return 0;
} }
static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length) static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length)

View File

@@ -95,12 +95,12 @@ LinuxEthernetTap::LinuxEthernetTap(
// Try to recall our last device name, or pick an unused one if that fails. // Try to recall our last device name, or pick an unused one if that fails.
bool recalledDevice = false; bool recalledDevice = false;
std::string devmapbuf; std::string devmapbuf;
Dictionary<8194> devmap; Dictionary devmap;
if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmapbuf)) { if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmapbuf)) {
devmap.load(devmapbuf.c_str()); devmap.fromString(devmapbuf);
char desiredDevice[128]; std::string desiredDevice(devmap.get(nwids,""));
if (devmap.get(nwids,desiredDevice,sizeof(desiredDevice)) > 0) { if (desiredDevice.length() > 2) {
Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),desiredDevice); Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),desiredDevice.c_str());
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);
recalledDevice = (stat(procpath,&sbuf) != 0); recalledDevice = (stat(procpath,&sbuf) != 0);
} }
@@ -172,18 +172,17 @@ LinuxEthernetTap::LinuxEthernetTap(
// Set close-on-exec so that devices cannot persist if we fork/exec for update // Set close-on-exec so that devices cannot persist if we fork/exec for update
::fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC); ::fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC);
(void)::pipe(_shutdownSignalPipe); ::pipe(_shutdownSignalPipe);
devmap.erase(nwids); devmap[nwids] = _dev;
devmap.add(nwids,_dev.c_str()); OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmap.toString());
OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),(const void *)devmap.data(),devmap.sizeBytes());
_thread = Thread::start(this); _thread = Thread::start(this);
} }
LinuxEthernetTap::~LinuxEthernetTap() LinuxEthernetTap::~LinuxEthernetTap()
{ {
(void)::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit ::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
Thread::join(_thread); Thread::join(_thread);
::close(_fd); ::close(_fd);
::close(_shutdownSignalPipe[0]); ::close(_shutdownSignalPipe[0]);
@@ -254,7 +253,7 @@ bool LinuxEthernetTap::removeIp(const InetAddress &ip)
if (!ip) if (!ip)
return true; return true;
std::vector<InetAddress> allIps(ips()); std::vector<InetAddress> allIps(ips());
if (std::find(allIps.begin(),allIps.end(),ip) != allIps.end()) { if (!std::binary_search(allIps.begin(),allIps.end(),ip)) {
if (___removeIp(_dev,ip)) if (___removeIp(_dev,ip))
return true; return true;
} }
@@ -294,7 +293,7 @@ std::vector<InetAddress> LinuxEthernetTap::ips() const
freeifaddrs(ifa); freeifaddrs(ifa);
std::sort(r.begin(),r.end()); std::sort(r.begin(),r.end());
r.erase(std::unique(r.begin(),r.end()),r.end()); std::unique(r.begin(),r.end());
return r; return r;
} }
@@ -308,7 +307,7 @@ void LinuxEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,
*((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType); *((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType);
memcpy(putBuf + 14,data,len); memcpy(putBuf + 14,data,len);
len += 14; len += 14;
(void)::write(_fd,putBuf,len); ::write(_fd,putBuf,len);
} }
} }
@@ -356,7 +355,7 @@ void LinuxEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,st
newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip)); newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
std::sort(newGroups.begin(),newGroups.end()); std::sort(newGroups.begin(),newGroups.end());
newGroups.erase(std::unique(newGroups.begin(),newGroups.end()),newGroups.end()); std::unique(newGroups.begin(),newGroups.end());
for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) { for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) {
if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m)) if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m))

View File

@@ -37,11 +37,7 @@
#endif #endif
#ifdef __WINDOWS__ #ifdef __WINDOWS__
#include <windows.h>
#include <wincrypt.h> #include <wincrypt.h>
#include <ShlObj.h>
#include <netioapi.h>
#include <iphlpapi.h>
#endif #endif
#include "OSUtils.hpp" #include "OSUtils.hpp"
@@ -107,86 +103,6 @@ std::vector<std::string> OSUtils::listDirectory(const char *path)
return r; return r;
} }
std::vector<std::string> OSUtils::listSubdirectories(const char *path)
{
std::vector<std::string> 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,".."))&&((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0))
r.push_back(std::string(ffd.cFileName));
} 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,".."))&&(dptr->d_type == DT_DIR))
r.push_back(std::string(dptr->d_name));
} 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))
break;
if ((dptr)&&(strcmp(dptr->d_name,".") != 0)&&(strcmp(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) {
if (!rmDashRf(p.c_str()))
return false;
}
} else break;
}
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__
@@ -311,42 +227,6 @@ bool OSUtils::writeFile(const char *path,const void *buf,unsigned int len)
return false; return false;
} }
std::string OSUtils::platformDefaultHomePath()
{
#ifdef __UNIX_LIKE__
#ifdef __APPLE__
// /Library/... on Apple
return std::string("/Library/Application Support/ZeroTier/One");
#else
#ifdef __BSD__
// BSD likes /var/db instead of /var/lib
return std::string("/var/db/zerotier-one");
#else
// Use /var/lib for Linux and other *nix
return std::string("/var/lib/zerotier-one");
#endif
#endif
#else // not __UNIX_LIKE__
#ifdef __WINDOWS__
// Look up app data folder on Windows, e.g. C:\ProgramData\...
char buf[16384];
if (SUCCEEDED(SHGetFolderPathA(NULL,CSIDL_COMMON_APPDATA,NULL,0,buf)))
return (std::string(buf) + "\\ZeroTier\\One");
else return std::string("C:\\ZeroTier\\One");
#else
return (std::string(ZT_PATH_SEPARATOR_S) + "ZeroTier" + ZT_PATH_SEPARATOR_S + "One"); // UNKNOWN PLATFORM
#endif
#endif // __UNIX_LIKE__ or not...
}
// 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 };

View File

@@ -105,26 +105,10 @@ 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 (without path prepended) * @return Names of files in directory
*/ */
static std::vector<std::string> listDirectory(const char *path); static std::vector<std::string> listDirectory(const char *path);
/**
* List a directory's subdirectories
*
* @param path Path to list
* @return Names of subdirectories (without path prepended)
*/
static std::vector<std::string> listSubdirectories(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
* *
@@ -251,11 +235,6 @@ public:
*/ */
static inline char toLower(char c) throw() { return (char)OSUtils::TOLOWER_TABLE[(unsigned long)c]; } static inline char toLower(char c) throw() { return (char)OSUtils::TOLOWER_TABLE[(unsigned long)c]; }
/**
* @return Platform default ZeroTier One home path
*/
static std::string platformDefaultHomePath();
private: private:
static const unsigned char TOLOWER_TABLE[256]; static const unsigned char TOLOWER_TABLE[256];
}; };

View File

@@ -34,6 +34,7 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
@@ -43,7 +44,6 @@
#include <net/if_arp.h> #include <net/if_arp.h>
#include <net/if_dl.h> #include <net/if_dl.h>
#include <net/if_media.h> #include <net/if_media.h>
#include <sys/sysctl.h>
#include <netinet6/in6_var.h> #include <netinet6/in6_var.h>
#include <netinet/in_var.h> #include <netinet/in_var.h>
#include <netinet/icmp6.h> #include <netinet/icmp6.h>
@@ -354,12 +354,12 @@ OSXEthernetTap::OSXEthernetTap(
// Try to reopen the last device we had, if we had one and it's still unused. // Try to reopen the last device we had, if we had one and it's still unused.
bool recalledDevice = false; bool recalledDevice = false;
std::string devmapbuf; std::string devmapbuf;
Dictionary<8194> devmap; Dictionary devmap;
if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmapbuf)) { if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmapbuf)) {
devmap.load(devmapbuf.c_str()); devmap.fromString(devmapbuf);
char desiredDevice[128]; std::string desiredDevice(devmap.get(nwids,""));
if (devmap.get(nwids,desiredDevice,sizeof(desiredDevice)) > 0) { if (desiredDevice.length() > 2) {
Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",desiredDevice); Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",desiredDevice.c_str());
if (stat(devpath,&stattmp) == 0) { if (stat(devpath,&stattmp) == 0) {
_fd = ::open(devpath,O_RDWR); _fd = ::open(devpath,O_RDWR);
if (_fd > 0) { if (_fd > 0) {
@@ -420,9 +420,8 @@ OSXEthernetTap::OSXEthernetTap(
++globalTapsRunning; ++globalTapsRunning;
devmap.erase(nwids); devmap[nwids] = _dev;
devmap.add(nwids,_dev.c_str()); OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmap.toString());
OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),(const void *)devmap.data(),devmap.sizeBytes());
_thread = Thread::start(this); _thread = Thread::start(this);
} }
@@ -474,7 +473,7 @@ bool OSXEthernetTap::addIp(const InetAddress &ip)
long cpid = (long)vfork(); long cpid = (long)vfork();
if (cpid == 0) { if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toString().c_str(),"alias",(const char *)0); ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
::_exit(-1); ::_exit(-1);
} else if (cpid > 0) { } else if (cpid > 0) {
int exitcode = -1; int exitcode = -1;
@@ -494,7 +493,7 @@ bool OSXEthernetTap::removeIp(const InetAddress &ip)
if (*i == ip) { if (*i == ip) {
long cpid = (long)vfork(); long cpid = (long)vfork();
if (cpid == 0) { if (cpid == 0) {
execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toIpString().c_str(),"-alias",(const char *)0); execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
_exit(-1); _exit(-1);
} else if (cpid > 0) { } else if (cpid > 0) {
int exitcode = -1; int exitcode = -1;

View File

@@ -252,7 +252,7 @@ public:
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
::send(_whackSendSocket,(const char *)this,1,0); ::send(_whackSendSocket,(const char *)this,1,0);
#else #else
(void)(::write(_whackSendSocket,(PhySocket *)this,1)); ::write(_whackSendSocket,(PhySocket *)this,1);
#endif #endif
} }
@@ -545,6 +545,7 @@ public:
#endif #endif
if (::bind(s,localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) { if (::bind(s,localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) {
perror("bind");
ZT_PHY_CLOSE_SOCKET(s); ZT_PHY_CLOSE_SOCKET(s);
return (PhySocket *)0; return (PhySocket *)0;
} }
@@ -917,7 +918,7 @@ public:
} }
if ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds))) { if ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds))) {
try { try {
_handler->phyOnTcpWritable((PhySocket *)&(*s),&(s->uptr)); _handler->phyOnTcpWritable((PhySocket *)&(*s),&(s->uptr),false);
} catch ( ... ) {} } catch ( ... ) {}
} }
} break; } break;

View File

@@ -41,19 +41,9 @@
#endif #endif
#endif #endif
#ifdef ZT_USE_SYSTEM_MINIUPNPC
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>
#else
#include "../ext/miniupnpc/miniupnpc.h" #include "../ext/miniupnpc/miniupnpc.h"
#include "../ext/miniupnpc/upnpcommands.h" #include "../ext/miniupnpc/upnpcommands.h"
#endif
#ifdef ZT_USE_SYSTEM_NATPMP
#include <natpmp.h>
#else
#include "../ext/libnatpmp/natpmp.h" #include "../ext/libnatpmp/natpmp.h"
#endif
namespace ZeroTier { namespace ZeroTier {

View File

@@ -125,18 +125,9 @@ public:
throw() throw()
{ {
memset(&_tid,0,sizeof(_tid)); memset(&_tid,0,sizeof(_tid));
pthread_attr_init(&_tattr);
// This corrects for systems with abnormally small defaults (musl) and also
// shrinks the stack on systems with large defaults to save a bit of memory.
pthread_attr_setstacksize(&_tattr,524288);
_started = false; _started = false;
} }
~Thread()
{
pthread_attr_destroy(&_tattr);
}
Thread(const Thread &t) Thread(const Thread &t)
throw() throw()
{ {
@@ -166,7 +157,7 @@ public:
{ {
Thread t; Thread t;
t._started = true; t._started = true;
if (pthread_create(&t._tid,&t._tattr,&___zt_threadMain<C>,instance)) if (pthread_create(&t._tid,(const pthread_attr_t *)0,&___zt_threadMain<C>,instance))
throw std::runtime_error("pthread_create() failed, unable to create thread"); throw std::runtime_error("pthread_create() failed, unable to create thread");
return t; return t;
} }
@@ -193,7 +184,6 @@ public:
private: private:
pthread_t _tid; pthread_t _tid;
pthread_attr_t _tattr;
volatile bool _started; volatile bool _started;
}; };

View File

@@ -861,14 +861,6 @@ void WindowsEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,
_multicastGroups.swap(newGroups); _multicastGroups.swap(newGroups);
} }
NET_IFINDEX WindowsEthernetTap::interfaceIndex() const
{
NET_IFINDEX idx = -1;
if (ConvertInterfaceLuidToIndex(&_deviceLuid,&idx) == NO_ERROR)
return idx;
return -1;
}
void WindowsEthernetTap::threadMain() void WindowsEthernetTap::threadMain()
throw() throw()
{ {

View File

@@ -105,7 +105,6 @@ public:
inline const NET_LUID &luid() const { return _deviceLuid; } inline const NET_LUID &luid() const { return _deviceLuid; }
inline const GUID &guid() const { return _deviceGuid; } inline const GUID &guid() const { return _deviceGuid; }
inline const std::string &instanceId() const { return _deviceInstanceId; } inline const std::string &instanceId() const { return _deviceInstanceId; }
NET_IFINDEX interfaceIndex() const;
void threadMain() void threadMain()
throw(); throw();

View File

@@ -53,6 +53,10 @@
#include "osdep/PortMapper.hpp" #include "osdep/PortMapper.hpp"
#include "osdep/Thread.hpp" #include "osdep/Thread.hpp"
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
#include "controller/SqliteNetworkController.hpp"
#endif // ZT_ENABLE_NETWORK_CONTROLLER
#ifdef __WINDOWS__ #ifdef __WINDOWS__
#include <tchar.h> #include <tchar.h>
#endif #endif
@@ -501,6 +505,19 @@ 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());
@@ -747,65 +764,28 @@ static int testOther()
} }
std::cout << "PASS" << std::endl; std::cout << "PASS" << std::endl;
std::cout << "[other] Testing/fuzzing Dictionary... "; std::cout.flush(); std::cout << "[other] Testing Dictionary... "; std::cout.flush();
for(int k=0;k<1000;++k) { for(int k=0;k<1000;++k) {
Dictionary<8194> test; Dictionary a,b;
char key[32][16]; int nk = rand() % 32;
char value[32][128]; for(int q=0;q<nk;++q) {
for(unsigned int q=0;q<32;++q) { std::string k,v;
Utils::snprintf(key[q],16,"%.8lx",(unsigned long)rand()); int kl = (rand() % 512);
int r = rand() % 128; int vl = (rand() % 512);
for(int x=0;x<r;++x) for(int i=0;i<kl;++i)
value[q][x] = ("0123456789\0\t\r\n= ")[rand() % 16]; k.push_back((char)rand());
value[q][r] = (char)0; for(int i=0;i<vl;++i)
test.add(key[q],value[q],r); v.push_back((char)rand());
a[k] = v;
} }
for(unsigned int q=0;q<1024;++q) { std::string aser = a.toString();
//int r = rand() % 128; b.fromString(aser);
int r = 31; if (a != b) {
char tmp[128]; std::cout << "FAIL!" << std::endl;
if (test.get(key[r],tmp,sizeof(tmp)) >= 0) {
if (strcmp(value[r],tmp)) {
std::cout << "FAILED (invalid value)!" << std::endl;
return -1;
}
} else {
std::cout << "FAILED (can't find key '" << key[r] << "')!" << std::endl;
return -1; return -1;
} }
} }
for(unsigned int q=0;q<31;++q) { std::cout << "PASS" << std::endl;
char tmp[128];
test.erase(key[q]);
if (test.get(key[q],tmp,sizeof(tmp)) >= 0) {
std::cout << "FAILED (key should have been erased)!" << std::endl;
return -1;
}
if (test.get(key[q+1],tmp,sizeof(tmp)) < 0) {
std::cout << "FAILED (key should NOT have been erased)!" << std::endl;
return -1;
}
}
}
int foo = 0;
volatile int *volatile bar = &foo; // force compiler not to optimize out test.get() below
for(int k=0;k<200;++k) {
int r = rand() % 8194;
unsigned char tmp[8194];
for(int q=0;q<r;++q)
tmp[q] = (unsigned char)((rand() % 254) + 1); // don't put nulls since those will always just terminate scan
tmp[r] = (r % 32) ? (char)(rand() & 0xff) : (char)0; // every 32nd iteration don't terminate the string maybe...
Dictionary<8194> test((const char *)tmp);
for(unsigned int q=0;q<100;++q) {
char tmp[128];
for(unsigned int x=0;x<128;++x)
tmp[x] = (char)(rand() & 0xff);
tmp[127] = (char)0;
char value[8194];
*bar += test.get(tmp,value,sizeof(value));
}
}
std::cout << "PASS (junk value to prevent optimization-out of test: " << foo << ")" << std::endl;
return 0; return 0;
} }
@@ -958,6 +938,42 @@ static int testPhy()
return 0; return 0;
} }
static int testSqliteNetworkController()
{
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
OSUtils::rm("./selftest_network_controller.db");
try {
std::cout << "[network-controller] Generating signing identity..." << std::endl;
Identity signingId;
signingId.generate();
{
std::cout << "[network-controller] Creating database..." << std::endl;
SqliteNetworkController controller("./selftest_network_controller.db");
std::cout << "[network-controller] Closing database..." << std::endl;
}
{
std::cout << "[network-controller] Re-opening database..." << std::endl;
SqliteNetworkController controller("./selftest_network_controller.db");
std::cout << "[network-controller] Closing database..." << std::endl;
}
} catch (std::runtime_error &exc) {
std::cout << "FAIL! (unexpected exception: " << exc.what() << ")" << std::endl;
return -1;
} catch ( ... ) {
std::cout << "FAIL! (unexpected exception: ...)" << std::endl;
return -1;
}
OSUtils::rm("./selftest_network_controller.db");
#endif // ZT_ENABLE_NETWORK_CONTROLLER
return 0;
}
static int testResolver() static int testResolver()
{ {
std::cout << "[resolver] Testing BackgroundResolver..."; std::cout.flush(); std::cout << "[resolver] Testing BackgroundResolver..."; std::cout.flush();
@@ -975,7 +991,6 @@ static int testResolver()
return 0; return 0;
} }
/*
static int testHttp() static int testHttp()
{ {
std::map<std::string,std::string> requestHeaders,responseHeaders; std::map<std::string,std::string> requestHeaders,responseHeaders;
@@ -1018,7 +1033,6 @@ static int testHttp()
return 0; return 0;
} }
*/
#ifdef __WINDOWS__ #ifdef __WINDOWS__
int _tmain(int argc, _TCHAR* argv[]) int _tmain(int argc, _TCHAR* argv[])
@@ -1076,6 +1090,7 @@ int main(int argc,char **argv)
srand((unsigned int)time(0)); srand((unsigned int)time(0));
///* ///*
r |= testSqliteNetworkController();
r |= testOther(); r |= testOther();
r |= testCrypto(); r |= testCrypto();
r |= testPacket(); r |= testPacket();

View File

@@ -22,16 +22,12 @@
#include "../version.h" #include "../version.h"
#include "../include/ZeroTierOne.h" #include "../include/ZeroTierOne.h"
#ifdef ZT_USE_SYSTEM_HTTP_PARSER
#include <http_parser.h>
#else
#include "../ext/http-parser/http_parser.h" #include "../ext/http-parser/http_parser.h"
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
#include "../controller/SqliteNetworkController.hpp"
#endif #endif
#include "../ext/json/json.hpp"
#include "../controller/EmbeddedNetworkController.hpp"
#include "../node/InetAddress.hpp" #include "../node/InetAddress.hpp"
#include "../node/Node.hpp" #include "../node/Node.hpp"
#include "../node/Utils.hpp" #include "../node/Utils.hpp"
@@ -59,6 +55,28 @@ static std::string _jsonEscape(const char *s)
} }
static std::string _jsonEscape(const std::string &s) { return _jsonEscape(s.c_str()); } static std::string _jsonEscape(const std::string &s) { return _jsonEscape(s.c_str()); }
static std::string _jsonEnumerate(const ZT_MulticastGroup *mg,unsigned int count)
{
std::string buf;
char tmp[128];
buf.push_back('[');
for(unsigned int i=0;i<count;++i) {
if (i > 0)
buf.push_back(',');
Utils::snprintf(tmp,sizeof(tmp),"\"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\\/%.8lx\"",
(unsigned int)((mg[i].mac >> 40) & 0xff),
(unsigned int)((mg[i].mac >> 32) & 0xff),
(unsigned int)((mg[i].mac >> 24) & 0xff),
(unsigned int)((mg[i].mac >> 16) & 0xff),
(unsigned int)((mg[i].mac >> 8) & 0xff),
(unsigned int)(mg[i].mac & 0xff),
(unsigned long)(mg[i].adi));
buf.append(tmp);
}
buf.push_back(']');
return buf;
}
static std::string _jsonEnumerate(const struct sockaddr_storage *ss,unsigned int count) static std::string _jsonEnumerate(const struct sockaddr_storage *ss,unsigned int count)
{ {
std::string buf; std::string buf;
@@ -73,30 +91,8 @@ static std::string _jsonEnumerate(const struct sockaddr_storage *ss,unsigned int
buf.push_back(']'); buf.push_back(']');
return buf; 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) static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_VirtualNetworkConfig *nc,const std::string &portDeviceName)
{ {
char json[4096]; char json[4096];
char prefix[32]; char prefix[32];
@@ -134,12 +130,9 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_VirtualNetw
"%s\t\"broadcastEnabled\": %s,\n" "%s\t\"broadcastEnabled\": %s,\n"
"%s\t\"portError\": %d,\n" "%s\t\"portError\": %d,\n"
"%s\t\"netconfRevision\": %lu,\n" "%s\t\"netconfRevision\": %lu,\n"
"%s\t\"multicastSubscriptions\": %s,\n"
"%s\t\"assignedAddresses\": %s,\n" "%s\t\"assignedAddresses\": %s,\n"
"%s\t\"routes\": %s,\n" "%s\t\"portDeviceName\": \"%s\"\n"
"%s\t\"portDeviceName\": \"%s\",\n"
"%s\t\"allowManaged\": %s,\n"
"%s\t\"allowGlobal\": %s,\n"
"%s\t\"allowDefault\": %s\n"
"%s}", "%s}",
prefix, prefix,
prefix,nc->nwid, prefix,nc->nwid,
@@ -153,19 +146,16 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_VirtualNetw
prefix,(nc->broadcastEnabled == 0) ? "false" : "true", prefix,(nc->broadcastEnabled == 0) ? "false" : "true",
prefix,nc->portError, prefix,nc->portError,
prefix,nc->netconfRevision, prefix,nc->netconfRevision,
prefix,_jsonEnumerate(nc->multicastSubscriptions,nc->multicastSubscriptionCount).c_str(),
prefix,_jsonEnumerate(nc->assignedAddresses,nc->assignedAddressCount).c_str(), prefix,_jsonEnumerate(nc->assignedAddresses,nc->assignedAddressCount).c_str(),
prefix,_jsonEnumerate(nc->routes,nc->routeCount).c_str(),
prefix,_jsonEscape(portDeviceName).c_str(), prefix,_jsonEscape(portDeviceName).c_str(),
prefix,(localSettings.allowManaged) ? "true" : "false",
prefix,(localSettings.allowGlobal) ? "true" : "false",
prefix,(localSettings.allowDefault) ? "true" : "false",
prefix); prefix);
buf.append(json); buf.append(json);
} }
static std::string _jsonEnumerate(unsigned int depth,const ZT_PeerPhysicalPath *pp,unsigned int count) static std::string _jsonEnumerate(unsigned int depth,const ZT_PeerPhysicalPath *pp,unsigned int count)
{ {
char json[2048]; char json[1024];
char prefix[32]; char prefix[32];
if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
@@ -184,17 +174,13 @@ static std::string _jsonEnumerate(unsigned int depth,const ZT_PeerPhysicalPath *
"%s\t\"lastSend\": %llu,\n" "%s\t\"lastSend\": %llu,\n"
"%s\t\"lastReceive\": %llu,\n" "%s\t\"lastReceive\": %llu,\n"
"%s\t\"active\": %s,\n" "%s\t\"active\": %s,\n"
"%s\t\"expired\": %s,\n" "%s\t\"preferred\": %s\n"
"%s\t\"preferred\": %s,\n"
"%s\t\"trustedPathId\": %llu\n"
"%s}", "%s}",
prefix,_jsonEscape(reinterpret_cast<const InetAddress *>(&(pp[i].address))->toString()).c_str(), prefix,_jsonEscape(reinterpret_cast<const InetAddress *>(&(pp[i].address))->toString()).c_str(),
prefix,pp[i].lastSend, prefix,pp[i].lastSend,
prefix,pp[i].lastReceive, prefix,pp[i].lastReceive,
prefix,(pp[i].expired != 0) ? "false" : "true", prefix,(pp[i].active == 0) ? "false" : "true",
prefix,(pp[i].expired == 0) ? "false" : "true",
prefix,(pp[i].preferred == 0) ? "false" : "true", prefix,(pp[i].preferred == 0) ? "false" : "true",
prefix,pp[i].trustedPathId,
prefix); prefix);
buf.append(json); buf.append(json);
} }
@@ -203,7 +189,7 @@ static std::string _jsonEnumerate(unsigned int depth,const ZT_PeerPhysicalPath *
static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer) static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer)
{ {
char json[2048]; char json[1024];
char prefix[32]; char prefix[32];
if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
@@ -215,7 +201,7 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer)
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_UPSTREAM: prole = "UPSTREAM"; break; case ZT_PEER_ROLE_RELAY: prole = "RELAY"; break;
case ZT_PEER_ROLE_ROOT: prole = "ROOT"; break; case ZT_PEER_ROLE_ROOT: prole = "ROOT"; break;
} }
@@ -250,7 +236,9 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer)
ControlPlane::ControlPlane(OneService *svc,Node *n,const char *uiStaticPath) : ControlPlane::ControlPlane(OneService *svc,Node *n,const char *uiStaticPath) :
_svc(svc), _svc(svc),
_node(n), _node(n),
_controller((EmbeddedNetworkController *)0), #ifdef ZT_ENABLE_NETWORK_CONTROLLER
_controller((SqliteNetworkController *)0),
#endif
_uiStaticPath((uiStaticPath) ? uiStaticPath : "") _uiStaticPath((uiStaticPath) ? uiStaticPath : "")
{ {
} }
@@ -432,9 +420,7 @@ unsigned int ControlPlane::handleRequest(
for(unsigned long i=0;i<nws->networkCount;++i) { for(unsigned long i=0;i<nws->networkCount;++i) {
if (i > 0) if (i > 0)
responseBody.append(","); responseBody.append(",");
OneService::NetworkSettings localSettings; _jsonAppend(1,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid));
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
_jsonAppend(1,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
} }
responseBody.append("\n]\n"); responseBody.append("\n]\n");
scode = 200; scode = 200;
@@ -444,9 +430,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) {
responseContentType = "application/json"; responseContentType = "application/json";
OneService::NetworkSettings localSettings; _jsonAppend(0,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid));
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
_jsonAppend(0,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
responseBody.push_back('\n'); responseBody.push_back('\n');
scode = 200; scode = 200;
break; break;
@@ -493,9 +477,13 @@ unsigned int ControlPlane::handleRequest(
responseContentType = "text/plain"; responseContentType = "text/plain";
scode = 200; 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
@@ -514,27 +502,8 @@ unsigned int ControlPlane::handleRequest(
if (nws) { if (nws) {
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) {
OneService::NetworkSettings localSettings;
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
try {
nlohmann::json j(nlohmann::json::parse(body));
if (j.is_object()) {
auto allowManaged = j["allowManaged"];
if (allowManaged.is_boolean()) localSettings.allowManaged = (bool)allowManaged;
auto allowGlobal = j["allowGlobal"];
if (allowGlobal.is_boolean()) localSettings.allowGlobal = (bool)allowGlobal;
auto allowDefault = j["allowDefault"];
if (allowDefault.is_boolean()) localSettings.allowDefault = (bool)allowDefault;
}
} catch ( ... ) {
// discard invalid JSON
}
_svc->setNetworkSettings(nws->networks[i].nwid,localSettings);
responseContentType = "application/json"; responseContentType = "application/json";
_jsonAppend(0,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings); _jsonAppend(0,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid));
responseBody.push_back('\n'); responseBody.push_back('\n');
scode = 200; scode = 200;
break; break;
@@ -544,9 +513,13 @@ unsigned int ControlPlane::handleRequest(
} else scode = 500; } else scode = 500;
} }
} 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
@@ -575,9 +548,13 @@ 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 { } else {

View File

@@ -31,7 +31,7 @@ namespace ZeroTier {
class OneService; class OneService;
class Node; class Node;
class EmbeddedNetworkController; class SqliteNetworkController;
struct InetAddress; struct InetAddress;
/** /**
@@ -43,16 +43,18 @@ public:
ControlPlane(OneService *svc,Node *n,const char *uiStaticPath); ControlPlane(OneService *svc,Node *n,const char *uiStaticPath);
~ControlPlane(); ~ControlPlane();
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
/** /**
* Set controller, which will be available under /controller * Set controller, which will be available under /controller
* *
* @param c Network controller instance * @param c Network controller instance
*/ */
inline void setController(EmbeddedNetworkController *c) inline void setController(SqliteNetworkController *c)
{ {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
_controller = c; _controller = c;
} }
#endif
/** /**
* Add an authentication token for API access * Add an authentication token for API access
@@ -87,7 +89,9 @@ public:
private: private:
OneService *const _svc; OneService *const _svc;
Node *const _node; Node *const _node;
EmbeddedNetworkController *_controller; #ifdef ZT_ENABLE_NETWORK_CONTROLLER
SqliteNetworkController *_controller;
#endif
std::string _uiStaticPath; std::string _uiStaticPath;
std::set<std::string> _authTokens; std::set<std::string> _authTokens;
Mutex _lock; Mutex _lock;

511
zerotierone/service/OneService.cpp Normal file → Executable file
View File

@@ -26,16 +26,11 @@
#include <set> #include <set>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <list>
#include "../version.h" #include "../version.h"
#include "../include/ZeroTierOne.h" #include "../include/ZeroTierOne.h"
#ifdef ZT_USE_SYSTEM_HTTP_PARSER
#include <http_parser.h>
#else
#include "../ext/http-parser/http_parser.h" #include "../ext/http-parser/http_parser.h"
#endif
#include "../node/Constants.hpp" #include "../node/Constants.hpp"
#include "../node/Mutex.hpp" #include "../node/Mutex.hpp"
@@ -52,7 +47,6 @@
#include "../osdep/BackgroundResolver.hpp" #include "../osdep/BackgroundResolver.hpp"
#include "../osdep/PortMapper.hpp" #include "../osdep/PortMapper.hpp"
#include "../osdep/Binder.hpp" #include "../osdep/Binder.hpp"
#include "../osdep/ManagedRoute.hpp"
#include "OneService.hpp" #include "OneService.hpp"
#include "ControlPlane.hpp" #include "ControlPlane.hpp"
@@ -69,7 +63,11 @@
*/ */
//#define ZT_BREAK_UDP //#define ZT_BREAK_UDP
#include "../controller/EmbeddedNetworkController.hpp" #ifdef ZT_ENABLE_NETWORK_CONTROLLER
#include "../controller/SqliteNetworkController.hpp"
#else
class SqliteNetworkController;
#endif // ZT_ENABLE_NETWORK_CONTROLLER
#ifdef __WINDOWS__ #ifdef __WINDOWS__
#include <WinSock2.h> #include <WinSock2.h>
@@ -82,18 +80,20 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#if !defined(__ANDROID__)
#include <ifaddrs.h> #include <ifaddrs.h>
#endif #endif
#endif
// Include the right tap device driver for this platform -- add new platforms here // Include the right tap device driver for this platform -- add new platforms here
//#ifdef ZT_SERVICE_NETCON #ifdef SDK
// In network containers builds, use the virtual netcon endpoint instead of a tun/tap port driver // In network containers builds, use the virtual netcon endpoint instead of a tun/tap port driver
#include "../../src/SDK_EthernetTap.hpp" #include "../src/SDK_EthernetTap.hpp"
namespace ZeroTier { typedef NetconEthernetTap EthernetTap; } namespace ZeroTier { typedef NetconEthernetTap EthernetTap; }
//#else // not ZT_SERVICE_NETCON so pick a tap driver #else // not ZT_SDK so pick a tap driver
/*
#ifdef __APPLE__ #ifdef __APPLE__
#include "../osdep/OSXEthernetTap.hpp" #include "../osdep/OSXEthernetTap.hpp"
namespace ZeroTier { typedef OSXEthernetTap EthernetTap; } namespace ZeroTier { typedef OSXEthernetTap EthernetTap; }
@@ -111,8 +111,7 @@ namespace ZeroTier { typedef WindowsEthernetTap EthernetTap; }
namespace ZeroTier { typedef BSDEthernetTap EthernetTap; } namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
#endif // __FreeBSD__ #endif // __FreeBSD__
#endif // ZT_SERVICE_NETCON #endif // ZT_SDK
*/
// Sanity limits for HTTP // Sanity limits for HTTP
#define ZT_MAX_HTTP_MESSAGE_SIZE (1024 * 1024 * 64) #define ZT_MAX_HTTP_MESSAGE_SIZE (1024 * 1024 * 64)
@@ -126,7 +125,7 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
#define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000 #define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000
// Path under ZT1 home for controller database if controller is enabled // Path under ZT1 home for controller database if controller is enabled
#define ZT_CONTROLLER_DB_PATH "controller.d" #define ZT_CONTROLLER_DB_PATH "controller.db"
// TCP fallback relay host -- geo-distributed using Amazon Route53 geo-aware DNS // TCP fallback relay host -- geo-distributed using Amazon Route53 geo-aware DNS
#define ZT_TCP_FALLBACK_RELAY "tcp-fallback.zerotier.com" #define ZT_TCP_FALLBACK_RELAY "tcp-fallback.zerotier.com"
@@ -198,33 +197,27 @@ public:
* *
* file=<filename> * file=<filename>
* signedBy=<signing identity> * signedBy=<signing identity>
* ed25519=<ed25519 ECC signature of archive in hex> * ed25519=<ed25519 ECC signature of archive>
* vMajor=<major version> * vMajor=<major version>
* vMinor=<minor version> * vMinor=<minor version>
* vRevision=<revision> */ * vRevision=<revision> */
Dictionary<4096> nfo(body.c_str()); Dictionary nfo(body);
char tmp[2048];
if (nfo.get("vMajor",tmp,sizeof(tmp)) <= 0) return; unsigned int vMajor = Utils::strToUInt(nfo.get("vMajor","0").c_str());
const unsigned int vMajor = Utils::strToUInt(tmp); unsigned int vMinor = Utils::strToUInt(nfo.get("vMinor","0").c_str());
if (nfo.get("vMinor",tmp,sizeof(tmp)) <= 0) return; unsigned int vRevision = Utils::strToUInt(nfo.get("vRevision","0").c_str());
const unsigned int vMinor = Utils::strToUInt(tmp);
if (nfo.get("vRevision",tmp,sizeof(tmp)) <= 0) return;
const unsigned int vRevision = Utils::strToUInt(tmp);
if (Utils::compareVersion(vMajor,vMinor,vRevision,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION) <= 0) { if (Utils::compareVersion(vMajor,vMinor,vRevision,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION) <= 0) {
//fprintf(stderr,"UPDATE %u.%u.%u is not newer than our version\n",vMajor,vMinor,vRevision); //fprintf(stderr,"UPDATE %u.%u.%u is not newer than our version\n",vMajor,vMinor,vRevision);
return; return;
} }
if (nfo.get("signedBy",tmp,sizeof(tmp)) <= 0) return;
Identity signedBy; Identity signedBy;
if ((!signedBy.fromString(tmp))||(!isValidSigningIdentity(signedBy))) { if ((!signedBy.fromString(nfo.get("signedBy","")))||(!isValidSigningIdentity(signedBy))) {
//fprintf(stderr,"UPDATE invalid signedBy or not authorized signing identity.\n"); //fprintf(stderr,"UPDATE invalid signedBy or not authorized signing identity.\n");
return; return;
} }
if (nfo.get("file",tmp,sizeof(tmp)) <= 0) return; std::string filePath(nfo.get("file",""));
std::string filePath(tmp);
if ((!filePath.length())||(filePath.find("..") != std::string::npos)) if ((!filePath.length())||(filePath.find("..") != std::string::npos))
return; return;
filePath = httpPath + filePath; filePath = httpPath + filePath;
@@ -235,8 +228,7 @@ public:
return; return;
} }
if (nfo.get("ed25519",tmp,sizeof(tmp)) <= 0) return; std::string ed25519(Utils::unhex(nfo.get("ed25519","")));
std::string ed25519(Utils::unhex(tmp));
if ((ed25519.length() == 0)||(!signedBy.verify(fileData.data(),(unsigned int)fileData.length(),ed25519.data(),(unsigned int)ed25519.length()))) { if ((ed25519.length() == 0)||(!signedBy.verify(fileData.data(),(unsigned int)fileData.length(),ed25519.data(),(unsigned int)ed25519.length()))) {
//fprintf(stderr,"UPDATE %s failed signature check!\n",filePath.c_str()); //fprintf(stderr,"UPDATE %s failed signature check!\n",filePath.c_str());
return; return;
@@ -412,18 +404,12 @@ static void StapFrameHandler(void *uptr,uint64_t nwid,const MAC &from,const MAC
static int ShttpOnMessageBegin(http_parser *parser); static int ShttpOnMessageBegin(http_parser *parser);
static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length); static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length);
#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2)
static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length); static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length);
#else
static int ShttpOnStatus(http_parser *parser);
#endif
static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length); static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length);
static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length); static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length);
static int ShttpOnHeadersComplete(http_parser *parser); static int ShttpOnHeadersComplete(http_parser *parser);
static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length); static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length);
static int ShttpOnMessageComplete(http_parser *parser); static int ShttpOnMessageComplete(http_parser *parser);
#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 1)
static const struct http_parser_settings HTTP_PARSER_SETTINGS = { static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
ShttpOnMessageBegin, ShttpOnMessageBegin,
ShttpOnUrl, ShttpOnUrl,
@@ -434,17 +420,6 @@ static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
ShttpOnBody, ShttpOnBody,
ShttpOnMessageComplete ShttpOnMessageComplete
}; };
#else
static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
ShttpOnMessageBegin,
ShttpOnUrl,
ShttpOnHeaderField,
ShttpOnValue,
ShttpOnHeadersComplete,
ShttpOnBody,
ShttpOnMessageComplete
};
#endif
struct TcpConnection struct TcpConnection
{ {
@@ -484,12 +459,14 @@ public:
const std::string _homePath; const std::string _homePath;
BackgroundResolver _tcpFallbackResolver; BackgroundResolver _tcpFallbackResolver;
EmbeddedNetworkController *_controller; #ifdef ZT_ENABLE_NETWORK_CONTROLLER
SqliteNetworkController *_controller;
#endif
Phy<OneServiceImpl *> _phy; Phy<OneServiceImpl *> _phy;
Node *_node; Node *_node;
/* /*
* To attempt to handle NAT/gateway craziness we use three local UDP ports: * To properly handle NAT/gateway craziness we use three local UDP ports:
* *
* [0] is the normal/default port, usually 9993 * [0] is the normal/default port, usually 9993
* [1] is a port dervied from our ZeroTier address * [1] is a port dervied from our ZeroTier address
@@ -523,26 +500,10 @@ public:
// Deadline for the next background task service function // Deadline for the next background task service function
volatile uint64_t _nextBackgroundTaskDeadline; volatile uint64_t _nextBackgroundTaskDeadline;
// Configured networks // Tap devices by network ID
struct NetworkState std::map< uint64_t,EthernetTap * > _taps;
{ std::map< uint64_t,std::vector<InetAddress> > _tapAssignedIps; // ZeroTier assigned IPs, not user or dhcp assigned
NetworkState() : Mutex _taps_m;
tap((EthernetTap *)0)
{
// Real defaults are in network 'up' code in network event handler
settings.allowManaged = true;
settings.allowGlobal = false;
settings.allowDefault = false;
}
EthernetTap *tap;
ZT_VirtualNetworkConfig config; // memcpy() of raw config from core
std::vector<InetAddress> managedIps;
std::list< SharedPtr<ManagedRoute> > managedRoutes;
NetworkSettings settings;
};
std::map<uint64_t,NetworkState> _nets;
Mutex _nets_m;
// Active TCP/IP connections // Active TCP/IP connections
std::set< TcpConnection * > _tcpConnections; // no mutex for this since it's done in the main loop thread only std::set< TcpConnection * > _tcpConnections; // no mutex for this since it's done in the main loop thread only
@@ -574,7 +535,9 @@ public:
OneServiceImpl(const char *hp,unsigned int port) : OneServiceImpl(const char *hp,unsigned int port) :
_homePath((hp) ? hp : ".") _homePath((hp) ? hp : ".")
,_tcpFallbackResolver(ZT_TCP_FALLBACK_RELAY) ,_tcpFallbackResolver(ZT_TCP_FALLBACK_RELAY)
,_controller((EmbeddedNetworkController *)0) #ifdef ZT_ENABLE_NETWORK_CONTROLLER
,_controller((SqliteNetworkController *)0)
#endif
,_phy(this,false,true) ,_phy(this,false,true)
,_node((Node *)0) ,_node((Node *)0)
,_controlPlane((ControlPlane *)0) ,_controlPlane((ControlPlane *)0)
@@ -666,7 +629,9 @@ public:
#ifdef ZT_USE_MINIUPNPC #ifdef ZT_USE_MINIUPNPC
delete _portMapper; delete _portMapper;
#endif #endif
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
delete _controller; delete _controller;
#endif
#ifdef ZT_ENABLE_CLUSTER #ifdef ZT_ENABLE_CLUSTER
delete _clusterDefinition; delete _clusterDefinition;
#endif #endif
@@ -677,6 +642,7 @@ public:
try { try {
std::string authToken; std::string authToken;
{ {
std::string tokenStr(_homePath + ZT_PATH_SEPARATOR_S + "authtoken.secret");
std::string authTokenPath(_homePath + ZT_PATH_SEPARATOR_S + "authtoken.secret"); std::string authTokenPath(_homePath + ZT_PATH_SEPARATOR_S + "authtoken.secret");
if (!OSUtils::readFile(authTokenPath.c_str(),authToken)) { if (!OSUtils::readFile(authTokenPath.c_str(),authToken)) {
unsigned char foo[24]; unsigned char foo[24];
@@ -696,9 +662,6 @@ public:
} }
authToken = _trimString(authToken); authToken = _trimString(authToken);
// Clean up any legacy files if present
OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S + "peers.save").c_str());
_node = new Node( _node = new Node(
OSUtils::now(), OSUtils::now(),
this, this,
@@ -753,40 +716,10 @@ public:
for(int i=0;i<3;++i) for(int i=0;i<3;++i)
_portsBE[i] = Utils::hton((uint16_t)_ports[i]); _portsBE[i] = Utils::hton((uint16_t)_ports[i]);
{ #ifdef ZT_ENABLE_NETWORK_CONTROLLER
FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S + "trustedpaths").c_str(),"r"); _controller = new SqliteNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str(),(_homePath + ZT_PATH_SEPARATOR_S + "circuitTestResults.d").c_str());
uint64_t ids[ZT_MAX_TRUSTED_PATHS];
InetAddress addresses[ZT_MAX_TRUSTED_PATHS];
if (trustpaths) {
char buf[1024];
unsigned int count = 0;
while ((fgets(buf,sizeof(buf),trustpaths))&&(count < ZT_MAX_TRUSTED_PATHS)) {
int fno = 0;
char *saveptr = (char *)0;
uint64_t trustedPathId = 0;
InetAddress trustedPathNetwork;
for(char *f=Utils::stok(buf,"=\r\n \t",&saveptr);(f);f=Utils::stok((char *)0,"=\r\n \t",&saveptr)) {
if (fno == 0) {
trustedPathId = Utils::hexStrToU64(f);
} else if (fno == 1) {
trustedPathNetwork = InetAddress(f);
} else break;
++fno;
}
if ( (trustedPathId != 0) && ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathNetwork.ipScope() != InetAddress::IP_SCOPE_GLOBAL) && (trustedPathNetwork.netmaskBits() > 0) ) {
ids[count] = trustedPathId;
addresses[count] = trustedPathNetwork;
++count;
}
}
fclose(trustpaths);
if (count)
_node->setTrustedPaths(reinterpret_cast<const struct sockaddr_storage *>(addresses),ids,count);
}
}
_controller = new EmbeddedNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str());
_node->setNetconfMaster((void *)_controller); _node->setNetconfMaster((void *)_controller);
#endif
#ifdef ZT_ENABLE_CLUSTER #ifdef ZT_ENABLE_CLUSTER
if (OSUtils::fileExists((_homePath + ZT_PATH_SEPARATOR_S + "cluster").c_str())) { if (OSUtils::fileExists((_homePath + ZT_PATH_SEPARATOR_S + "cluster").c_str())) {
@@ -839,7 +772,10 @@ public:
_controlPlane = new ControlPlane(this,_node,(_homePath + ZT_PATH_SEPARATOR_S + "ui").c_str()); _controlPlane = new ControlPlane(this,_node,(_homePath + ZT_PATH_SEPARATOR_S + "ui").c_str());
_controlPlane->addAuthToken(authToken.c_str()); _controlPlane->addAuthToken(authToken.c_str());
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
_controlPlane->setController(_controller); _controlPlane->setController(_controller);
#endif
{ // Remember networks from previous session { // Remember networks from previous session
std::vector<std::string> networksDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str())); std::vector<std::string> networksDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str()));
@@ -850,6 +786,10 @@ public:
} }
} }
// Start two background threads to handle expensive ops out of line
Thread::start(_node);
Thread::start(_node);
_nextBackgroundTaskDeadline = 0; _nextBackgroundTaskDeadline = 0;
uint64_t clockShouldBe = OSUtils::now(); uint64_t clockShouldBe = OSUtils::now();
_lastRestart = clockShouldBe; _lastRestart = clockShouldBe;
@@ -881,7 +821,7 @@ public:
restarted = true; restarted = true;
} }
// Refresh bindings in case device's interfaces have changed, and also sync routes to update any shadow routes (e.g. shadow default) // Refresh bindings in case device's interfaces have changed
if (((now - lastBindRefresh) >= ZT_BINDER_REFRESH_PERIOD)||(restarted)) { if (((now - lastBindRefresh) >= ZT_BINDER_REFRESH_PERIOD)||(restarted)) {
lastBindRefresh = now; lastBindRefresh = now;
for(int i=0;i<3;++i) { for(int i=0;i<3;++i) {
@@ -889,13 +829,6 @@ public:
_bindings[i].refresh(_phy,_ports[i],*this); _bindings[i].refresh(_phy,_ports[i],*this);
} }
} }
{
Mutex::Lock _l(_nets_m);
for(std::map<uint64_t,NetworkState>::iterator n(_nets.begin());n!=_nets.end();++n) {
if (n->second.tap)
syncManagedStuff(n->second,false,true);
}
}
} }
uint64_t dl = _nextBackgroundTaskDeadline; uint64_t dl = _nextBackgroundTaskDeadline;
@@ -921,16 +854,14 @@ public:
if ((now - lastTapMulticastGroupCheck) >= ZT_TAP_CHECK_MULTICAST_INTERVAL) { if ((now - lastTapMulticastGroupCheck) >= ZT_TAP_CHECK_MULTICAST_INTERVAL) {
lastTapMulticastGroupCheck = now; lastTapMulticastGroupCheck = now;
Mutex::Lock _l(_nets_m); Mutex::Lock _l(_taps_m);
for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) { for(std::map< uint64_t,EthernetTap *>::const_iterator t(_taps.begin());t!=_taps.end();++t) {
if (n->second.tap) {
std::vector<MulticastGroup> added,removed; std::vector<MulticastGroup> added,removed;
n->second.tap->scanMulticastGroups(added,removed); t->second->scanMulticastGroups(added,removed);
for(std::vector<MulticastGroup>::iterator m(added.begin());m!=added.end();++m) for(std::vector<MulticastGroup>::iterator m(added.begin());m!=added.end();++m)
_node->multicastSubscribe(n->first,m->mac().toInt(),m->adi()); _node->multicastSubscribe(t->first,m->mac().toInt(),m->adi());
for(std::vector<MulticastGroup>::iterator m(removed.begin());m!=removed.end();++m) for(std::vector<MulticastGroup>::iterator m(removed.begin());m!=removed.end();++m)
_node->multicastUnsubscribe(n->first,m->mac().toInt(),m->adi()); _node->multicastUnsubscribe(t->first,m->mac().toInt(),m->adi());
}
} }
} }
@@ -972,10 +903,10 @@ public:
} catch ( ... ) {} } catch ( ... ) {}
{ {
Mutex::Lock _l(_nets_m); Mutex::Lock _l(_taps_m);
for(std::map<uint64_t,NetworkState>::iterator n(_nets.begin());n!=_nets.end();++n) for(std::map< uint64_t,EthernetTap * >::iterator t(_taps.begin());t!=_taps.end();++t)
delete n->second.tap; delete t->second;
_nets.clear(); _taps.clear();
} }
delete _controlPlane; delete _controlPlane;
@@ -1000,11 +931,11 @@ public:
virtual std::string portDeviceName(uint64_t nwid) const virtual std::string portDeviceName(uint64_t nwid) const
{ {
Mutex::Lock _l(_nets_m); Mutex::Lock _l(_taps_m);
std::map<uint64_t,NetworkState>::const_iterator n(_nets.find(nwid)); std::map< uint64_t,EthernetTap * >::const_iterator t(_taps.find(nwid));
if ((n != _nets.end())&&(n->second.tap)) if (t != _taps.end())
return n->second.tap->deviceName(); return t->second->deviceName();
else return std::string(); return std::string();
} }
virtual bool tcpFallbackActive() const virtual bool tcpFallbackActive() const
@@ -1020,173 +951,35 @@ public:
_phy.whack(); _phy.whack();
} }
virtual bool getNetworkSettings(const uint64_t nwid,NetworkSettings &settings) const // For ZT SDK API
virtual void join(const char *hp)
{ {
Mutex::Lock _l(_nets_m); _node->join(Utils::hexStrToU64(hp),NULL);
std::map<uint64_t,NetworkState>::const_iterator n(_nets.find(nwid));
if (n == _nets.end())
return false;
memcpy(&settings,&(n->second.settings),sizeof(NetworkSettings));
return true;
} }
virtual bool setNetworkSettings(const uint64_t nwid,const NetworkSettings &settings) virtual void leave(const char *hp)
{ {
Mutex::Lock _l(_nets_m); _node->leave(Utils::hexStrToU64(hp),NULL);
std::map<uint64_t,NetworkState>::iterator n(_nets.find(nwid));
if (n == _nets.end())
return false;
memcpy(&(n->second.settings),&settings,sizeof(NetworkSettings));
char nlcpath[256];
Utils::snprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
FILE *out = fopen(nlcpath,"w");
if (out) {
fprintf(out,"allowManaged=%d\n",(int)n->second.settings.allowManaged);
fprintf(out,"allowGlobal=%d\n",(int)n->second.settings.allowGlobal);
fprintf(out,"allowDefault=%d\n",(int)n->second.settings.allowDefault);
fclose(out);
} }
if (n->second.tap) virtual std::string givenHomePath()
syncManagedStuff(n->second,true,true); {
return _homePath;
}
return true; virtual std::map< uint64_t,EthernetTap * > getTaps()
{
return _taps;
}
virtual Node * getNode()
{
return _node;
} }
// Begin private implementation methods // Begin private implementation methods
// Checks if a managed IP or route target is allowed
bool checkIfManagedIsAllowed(const NetworkState &n,const InetAddress &target)
{
if (!n.settings.allowManaged)
return false;
if (target.isDefaultRoute())
return n.settings.allowDefault;
switch(target.ipScope()) {
case InetAddress::IP_SCOPE_NONE:
case InetAddress::IP_SCOPE_MULTICAST:
case InetAddress::IP_SCOPE_LOOPBACK:
case InetAddress::IP_SCOPE_LINK_LOCAL:
return false;
case InetAddress::IP_SCOPE_GLOBAL:
return n.settings.allowGlobal;
default:
return true;
}
}
// Match only an IP from a vector of IPs -- used in syncManagedStuff()
bool matchIpOnly(const std::vector<InetAddress> &ips,const InetAddress &ip) const
{
for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
if (i->ipsEqual(ip))
return true;
}
return false;
}
// Apply or update managed IPs for a configured network (be sure n.tap exists)
void syncManagedStuff(NetworkState &n,bool syncIps,bool syncRoutes)
{
// assumes _nets_m is locked
if (syncIps) {
std::vector<InetAddress> newManagedIps;
newManagedIps.reserve(n.config.assignedAddressCount);
for(unsigned int i=0;i<n.config.assignedAddressCount;++i) {
const InetAddress *ii = reinterpret_cast<const InetAddress *>(&(n.config.assignedAddresses[i]));
if (checkIfManagedIsAllowed(n,*ii))
newManagedIps.push_back(*ii);
}
std::sort(newManagedIps.begin(),newManagedIps.end());
newManagedIps.erase(std::unique(newManagedIps.begin(),newManagedIps.end()),newManagedIps.end());
for(std::vector<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
if (std::find(newManagedIps.begin(),newManagedIps.end(),*ip) == newManagedIps.end()) {
if (!n.tap->removeIp(*ip))
fprintf(stderr,"ERROR: unable to remove ip address %s" ZT_EOL_S, ip->toString().c_str());
}
}
for(std::vector<InetAddress>::iterator ip(newManagedIps.begin());ip!=newManagedIps.end();++ip) {
if (std::find(n.managedIps.begin(),n.managedIps.end(),*ip) == n.managedIps.end()) {
if (!n.tap->addIp(*ip))
fprintf(stderr,"ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString().c_str());
}
}
n.managedIps.swap(newManagedIps);
}
if (syncRoutes) {
char tapdev[64];
#ifdef __WINDOWS__
Utils::snprintf(tapdev,sizeof(tapdev),"%.16llx",(unsigned long long)n.tap->luid().Value);
#else
Utils::scopy(tapdev,sizeof(tapdev),n.tap->deviceName().c_str());
#endif
std::vector<InetAddress> myIps(n.tap->ips());
// Nuke applied routes that are no longer in n.config.routes[] and/or are not allowed
for(std::list< SharedPtr<ManagedRoute> >::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();) {
bool haveRoute = false;
if ( (checkIfManagedIsAllowed(n,(*mr)->target())) && (((*mr)->via().ss_family != (*mr)->target().ss_family)||(!matchIpOnly(myIps,(*mr)->via()))) ) {
for(unsigned int i=0;i<n.config.routeCount;++i) {
const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
if ( ((*mr)->target() == *target) && ( ((via->ss_family == target->ss_family)&&((*mr)->via().ipsEqual(*via))) || (tapdev == (*mr)->device()) ) ) {
haveRoute = true;
break;
}
}
}
if (haveRoute) {
++mr;
} else {
n.managedRoutes.erase(mr++);
}
}
// Apply routes in n.config.routes[] that we haven't applied yet, and sync those we have in case shadow routes need to change
for(unsigned int i=0;i<n.config.routeCount;++i) {
const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
if ( (!checkIfManagedIsAllowed(n,*target)) || ((via->ss_family == target->ss_family)&&(matchIpOnly(myIps,*via))) )
continue;
bool haveRoute = false;
// Ignore routes implied by local managed IPs since adding the IP adds the route
for(std::vector<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
if ((target->netmaskBits() == ip->netmaskBits())&&(target->containsAddress(*ip))) {
haveRoute = true;
break;
}
}
if (haveRoute)
continue;
// If we've already applied this route, just sync it and continue
for(std::list< SharedPtr<ManagedRoute> >::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();++mr) {
if ( ((*mr)->target() == *target) && ( ((via->ss_family == target->ss_family)&&((*mr)->via().ipsEqual(*via))) || (tapdev == (*mr)->device()) ) ) {
haveRoute = true;
(*mr)->sync();
break;
}
}
if (haveRoute)
continue;
// Add and apply new routes
n.managedRoutes.push_back(SharedPtr<ManagedRoute>(new ManagedRoute(*target,*via,tapdev)));
if (!n.managedRoutes.back()->sync())
n.managedRoutes.pop_back();
}
}
}
inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len) inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
{ {
#ifdef ZT_ENABLE_CLUSTER #ifdef ZT_ENABLE_CLUSTER
@@ -1389,7 +1182,7 @@ public:
} }
} }
inline void phyOnTcpWritable(PhySocket *sock,void **uptr) inline void phyOnTcpWritable(PhySocket *sock,void **uptr,bool lwip_invoked)
{ {
TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr); TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr);
Mutex::Lock _l(tc->writeBuf_m); Mutex::Lock _l(tc->writeBuf_m);
@@ -1419,17 +1212,15 @@ public:
inline int nodeVirtualNetworkConfigFunction(uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwc) inline int nodeVirtualNetworkConfigFunction(uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwc)
{ {
Mutex::Lock _l(_nets_m); Mutex::Lock _l(_taps_m);
NetworkState &n = _nets[nwid]; std::map< uint64_t,EthernetTap * >::iterator t(_taps.find(nwid));
switch(op) { switch(op) {
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP: case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP:
if (!n.tap) { if (t == _taps.end()) {
try { try {
char friendlyName[128]; char friendlyName[1024];
Utils::snprintf(friendlyName,sizeof(friendlyName),"ZeroTier One [%.16llx]",nwid); Utils::snprintf(friendlyName,sizeof(friendlyName),"ZeroTier One [%.16llx]",nwid);
n.tap = new EthernetTap( t = _taps.insert(std::pair< uint64_t,EthernetTap *>(nwid,new EthernetTap(
_homePath.c_str(), _homePath.c_str(),
MAC(nwc->mac), MAC(nwc->mac),
nwc->mtu, nwc->mtu,
@@ -1437,19 +1228,8 @@ public:
nwid, nwid,
friendlyName, friendlyName,
StapFrameHandler, StapFrameHandler,
(void *)this); (void *)this))).first;
*nuptr = (void *)&n; *nuptr = (void *)t->second;
char nlcpath[256];
Utils::snprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
std::string nlcbuf;
if (OSUtils::readFile(nlcpath,nlcbuf)) {
Dictionary<4096> nc;
nc.load(nlcbuf.c_str());
n.settings.allowManaged = nc.getB("allowManaged",true);
n.settings.allowGlobal = nc.getB("allowGlobal",false);
n.settings.allowDefault = nc.getB("allowDefault",false);
}
} catch (std::exception &exc) { } catch (std::exception &exc) {
#ifdef __WINDOWS__ #ifdef __WINDOWS__
FILE *tapFailLog = fopen((_homePath + ZT_PATH_SEPARATOR_S"port_error_log.txt").c_str(),"a"); FILE *tapFailLog = fopen((_homePath + ZT_PATH_SEPARATOR_S"port_error_log.txt").c_str(),"a");
@@ -1460,47 +1240,53 @@ public:
#else #else
fprintf(stderr,"ERROR: unable to configure virtual network port: %s" ZT_EOL_S,exc.what()); fprintf(stderr,"ERROR: unable to configure virtual network port: %s" ZT_EOL_S,exc.what());
#endif #endif
_nets.erase(nwid);
return -999; return -999;
} catch ( ... ) { } catch ( ... ) {
return -999; // tap init failed return -999; // tap init failed
} }
} }
// After setting up tap, fall through to CONFIG_UPDATE since we also want to do this... // fall through...
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE: case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE:
memcpy(&(n.config),nwc,sizeof(ZT_VirtualNetworkConfig)); if (t != _taps.end()) {
if (n.tap) { // sanity check t->second->setEnabled(nwc->enabled != 0);
syncManagedStuff(n,true,true);
std::vector<InetAddress> &assignedIps = _tapAssignedIps[nwid];
std::vector<InetAddress> newAssignedIps;
for(unsigned int i=0;i<nwc->assignedAddressCount;++i)
newAssignedIps.push_back(InetAddress(nwc->assignedAddresses[i]));
std::sort(newAssignedIps.begin(),newAssignedIps.end());
newAssignedIps.erase(std::unique(newAssignedIps.begin(),newAssignedIps.end()),newAssignedIps.end());
for(std::vector<InetAddress>::iterator ip(newAssignedIps.begin());ip!=newAssignedIps.end();++ip) {
if (!std::binary_search(assignedIps.begin(),assignedIps.end(),*ip))
if (!t->second->addIp(*ip))
fprintf(stderr,"ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString().c_str());
}
for(std::vector<InetAddress>::iterator ip(assignedIps.begin());ip!=assignedIps.end();++ip) {
if (!std::binary_search(newAssignedIps.begin(),newAssignedIps.end(),*ip))
if (!t->second->removeIp(*ip))
fprintf(stderr,"ERROR: unable to remove ip address %s" ZT_EOL_S, ip->toString().c_str());
}
assignedIps.swap(newAssignedIps);
} else { } else {
_nets.erase(nwid);
return -999; // tap init failed return -999; // tap init failed
} }
break; break;
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN: case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN:
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY: case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY:
if (n.tap) { // sanity check if (t != _taps.end()) {
#ifdef __WINDOWS__ #ifdef __WINDOWS__
std::string winInstanceId(n.tap->instanceId()); std::string winInstanceId(t->second->instanceId());
#endif #endif
*nuptr = (void *)0; *nuptr = (void *)0;
delete n.tap; delete t->second;
_nets.erase(nwid); _taps.erase(t);
_tapAssignedIps.erase(nwid);
#ifdef __WINDOWS__ #ifdef __WINDOWS__
if ((op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY)&&(winInstanceId.length() > 0)) if ((op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY)&&(winInstanceId.length() > 0))
WindowsEthernetTap::deletePersistentTapDevice(winInstanceId.c_str()); WindowsEthernetTap::deletePersistentTapDevice(winInstanceId.c_str());
#endif #endif
if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY) {
char nlcpath[256];
Utils::snprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
OSUtils::rm(nlcpath);
}
} else {
_nets.erase(nwid);
} }
break; break;
} }
return 0; return 0;
} }
@@ -1660,19 +1446,18 @@ public:
inline void nodeVirtualNetworkFrameFunction(uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) inline void nodeVirtualNetworkFrameFunction(uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{ {
NetworkState *n = reinterpret_cast<NetworkState *>(*nuptr); EthernetTap *tap = reinterpret_cast<EthernetTap *>(*nuptr);
if ((!n)||(!n->tap)) if (!tap)
return; return;
n->tap->put(MAC(sourceMac),MAC(destMac),etherType,data,len); tap->put(MAC(sourceMac),MAC(destMac),etherType,data,len);
} }
inline int nodePathCheckFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr) inline int nodePathCheckFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
{ {
Mutex::Lock _l(_nets_m); Mutex::Lock _l(_taps_m);
for(std::map< uint64_t,EthernetTap * >::const_iterator t(_taps.begin());t!=_taps.end();++t) {
for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) { if (t->second) {
if (n->second.tap) { std::vector<InetAddress> ips(t->second->ips());
std::vector<InetAddress> ips(n->second.tap->ips());
for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) { for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
if (i->containsAddress(*(reinterpret_cast<const InetAddress *>(remoteAddr)))) { if (i->containsAddress(*(reinterpret_cast<const InetAddress *>(remoteAddr)))) {
return 0; return 0;
@@ -1680,13 +1465,6 @@ public:
} }
} }
} }
/* Note: I do not think we need to scan for overlap with managed routes
* because of the "route forking" and interface binding that we do. This
* ensures (we hope) that ZeroTier traffic will still take the physical
* path even if its managed routes override this for other traffic. Will
* revisit if we see problems with this. */
return 1; return 1;
} }
@@ -1706,11 +1484,7 @@ public:
if (_controlPlane) if (_controlPlane)
scode = _controlPlane->handleRequest(tc->from,tc->parser.method,tc->url,tc->headers,tc->body,data,contentType); scode = _controlPlane->handleRequest(tc->from,tc->parser.method,tc->url,tc->headers,tc->body,data,contentType);
else scode = 500; else scode = 500;
} catch (std::exception &exc) {
fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: %s" ZT_EOL_S,exc.what());
scode = 500;
} catch ( ... ) { } catch ( ... ) {
fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: unknown exceptino" ZT_EOL_S);
scode = 500; scode = 500;
} }
@@ -1756,10 +1530,10 @@ public:
if (isBlacklistedLocalInterfaceForZeroTierTraffic(ifname)) if (isBlacklistedLocalInterfaceForZeroTierTraffic(ifname))
return false; return false;
Mutex::Lock _l(_nets_m); Mutex::Lock _l(_taps_m);
for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) { for(std::map< uint64_t,EthernetTap * >::const_iterator t(_taps.begin());t!=_taps.end();++t) {
if (n->second.tap) { if (t->second) {
std::vector<InetAddress> ips(n->second.tap->ips()); std::vector<InetAddress> ips(t->second->ips());
for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) { for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
if (i->ipsEqual(ifaddr)) if (i->ipsEqual(ifaddr))
return false; return false;
@@ -1877,19 +1651,13 @@ static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length)
tc->url.append(ptr,length); tc->url.append(ptr,length);
return 0; return 0;
} }
#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2)
static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length) static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length)
#else
static int ShttpOnStatus(http_parser *parser)
#endif
{ {
/*
TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data); TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data);
tc->messageSize += (unsigned long)length; tc->messageSize += (unsigned long)length;
if (tc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE) if (tc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE)
return -1; return -1;
tc->status.append(ptr,length); tc->status.append(ptr,length);
*/
return 0; return 0;
} }
static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length) static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length)
@@ -1949,7 +1717,38 @@ static int ShttpOnMessageComplete(http_parser *parser)
std::string OneService::platformDefaultHomePath() std::string OneService::platformDefaultHomePath()
{ {
return OSUtils::platformDefaultHomePath(); #ifdef __UNIX_LIKE__
#ifdef __APPLE__
// /Library/... on Apple
return std::string("/Library/Application Support/ZeroTier/One");
#else
#ifdef __BSD__
// BSD likes /var/db instead of /var/lib
return std::string("/var/db/zerotier-one");
#else
// Use /var/lib for Linux and other *nix
return std::string("/var/lib/zerotier-one");
#endif
#endif
#else // not __UNIX_LIKE__
#ifdef __WINDOWS__
// Look up app data folder on Windows, e.g. C:\ProgramData\...
char buf[16384];
if (SUCCEEDED(SHGetFolderPathA(NULL,CSIDL_COMMON_APPDATA,NULL,0,buf)))
return (std::string(buf) + "\\ZeroTier\\One");
else return std::string("C:\\ZeroTier\\One");
#else
return std::string(); // UNKNOWN PLATFORM
#endif
#endif // __UNIX_LIKE__ or not...
} }
std::string OneService::autoUpdateUrl() std::string OneService::autoUpdateUrl()

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