summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--controller/README.md257
-rw-r--r--controller/SqliteNetworkController.cpp303
-rw-r--r--controller/SqliteNetworkController.hpp1
-rw-r--r--debian/changelog19
-rw-r--r--debian/control4
-rw-r--r--debian/control.wheezy19
-rwxr-xr-xdebian/rules.wheezy11
-rwxr-xr-xext/installfiles/mac/ZeroTier One.pkgproj2
-rw-r--r--include/ZeroTierOne.h33
-rw-r--r--java/jni/Android.mk1
-rw-r--r--java/jni/ZT_jniutils.cpp97
-rw-r--r--java/jni/ZT_jniutils.h2
-rw-r--r--java/jni/com_zerotierone_sdk_Node.cpp5
-rw-r--r--java/src/com/zerotier/sdk/MulticastGroup.java53
-rw-r--r--java/src/com/zerotier/sdk/Version.java1
-rw-r--r--java/src/com/zerotier/sdk/VirtualNetworkConfig.java15
-rw-r--r--linux-build-farm/README.md2
-rw-r--r--linux-build-farm/debian-jessie/x64/Dockerfile2
-rw-r--r--linux-build-farm/debian-jessie/x86/Dockerfile2
-rw-r--r--linux-build-farm/debian-stretch/x64/Dockerfile12
-rw-r--r--linux-build-farm/debian-stretch/x86/Dockerfile12
-rw-r--r--linux-build-farm/debian-wheezy/x64/Dockerfile12
-rw-r--r--linux-build-farm/debian-wheezy/x86/Dockerfile15
-rw-r--r--linux-build-farm/fedora-22/x64/Dockerfile3
-rw-r--r--linux-build-farm/fedora-22/x86/Dockerfile5
-rwxr-xr-xlinux-build-farm/make-apt-repos.sh16
-rwxr-xr-xlinux-build-farm/make-rpm-repos.sh64
-rw-r--r--linux-build-farm/ubuntu-trusty/x64/Dockerfile2
-rw-r--r--linux-build-farm/ubuntu-trusty/x86/Dockerfile2
-rw-r--r--linux-build-farm/ubuntu-wily/x64/Dockerfile2
-rw-r--r--linux-build-farm/ubuntu-wily/x86/Dockerfile2
-rw-r--r--linux-build-farm/ubuntu-xenial/x64/Dockerfile8
-rw-r--r--linux-build-farm/ubuntu-xenial/x86/Dockerfile8
-rw-r--r--make-linux.mk4
-rw-r--r--node/IncomingPacket.cpp28
-rw-r--r--node/Node.cpp13
-rw-r--r--node/Node.hpp13
-rw-r--r--node/Packet.hpp50
-rw-r--r--node/README.md14
-rw-r--r--node/Switch.cpp7
-rw-r--r--node/Topology.hpp53
-rw-r--r--one.cpp168
-rw-r--r--osdep/LinuxEthernetTap.cpp6
-rw-r--r--osdep/ManagedRoute.cpp4
-rw-r--r--osdep/ManagedRoute.hpp4
-rw-r--r--osdep/OSXEthernetTap.cpp4
-rw-r--r--selftest.cpp37
-rw-r--r--service/ControlPlane.cpp4
-rw-r--r--service/OneService.cpp66
-rw-r--r--service/README.md130
-rw-r--r--version.h2
-rw-r--r--zerotier-one.spec12
52 files changed, 1010 insertions, 601 deletions
diff --git a/controller/README.md b/controller/README.md
index 6989e98d..8b789a3e 100644
--- a/controller/README.md
+++ b/controller/README.md
@@ -1,32 +1,261 @@
-Network Controller Implementation
+Network Controller Microservice
======
-This folder contains code implementing the node/NetworkController.hpp interface to allow ZeroTier nodes to create and manage virtual networks.
+ZeroTier's 16-digit network IDs are really just a concatenation of the 10-digit ZeroTier address of a network controller followed by a 6-digit (24-bit) network number on that controller. Fans of software defined networking will recognize this as a variation of the familiar [separation of data plane and control plane](http://sdntutorials.com/difference-between-control-plane-and-data-plane/) SDN design pattern.
+
+This code implements the *node/NetworkController.hpp* interface and provides a SQLite3-backed network controller microservice. Including it in the build allows ZeroTier One to act as a controller and create/manage networks.
+
+This is the same code we use to run [my.zerotier.com](https://my.zerotier.com/), which is a web UI and API that runs in front of a pool of controllers.
### Building
-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.
+On Linux, Mac, or BSD you can create a controller-enabled build with:
+
+ make ZT_ENABLE_NETWORK_CONTROLLER=1
+
+You will need the development headers and libraries for SQLite3 installed.
### Running
-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.
+After building and installing (`make install`) a controller-enabled build of ZeroTier One, start it and try:
+
+ sudo zerotier-cli /controller
+
+You should see something like:
+
+ {
+ "controller": true,
+ "apiVersion": 2,
+ "clock": 1468002975497,
+ "instanceId": "8ab354604debe1da27ee627c9ef94a48"
+ }
+
+When started, a controller-enabled build of ZeroTier One will automatically create and initialize a `controller.db` file in its home folder. This is where all the controller's data and persistent state lives. If you're upgrading an old controller it will upgrade its database schema automatically on first launch. Make a backup of the old controller's database first since you can't go backward.
+
+Controllers periodically make backups of their database as `controller.db.backup`. This is done so that this file can be more easily copied/rsync'ed to other systems without worrying about corruption. SQLite3 supports multiple processes accessing the same database file, so `sqlite3 /path/to/controller.db .dump` also works but can be slow on a busy controller.
+
+Controllers can in theory host up to 2^24 networks and serve many millions of devices (or more), but we recommend running multiple controllers for a lot of networks to spread load and be more fault tolerant.
+
+### Dockerizing Controllers
+
+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.
+
+### Implementing High Availability Fail-Over
+
+ZeroTier network controllers are not single points of failure for networks-- in the sense that if a controller goes down *existing* members of a network can continue to communicate. But new members (or those that have been offline for a while) can't join, existing members can't be de-authorized, and other changes to the network's configuration can't be made. This means that short "glitches" in controller availability are not a major problem but long periods of unavailability can be.
+
+Because controllers are just regular ZeroTier nodes and controller queries are in-band, controllers can trivially be moved without worrying about changes to underlying physical IPs. This makes high-availability fail-over very easy to implement.
+
+Just set up two cloud hosts, preferably in different data centers (e.g. two different AWS regions or Digital Ocean SF and NYC). Now set up the hot spare controller to constantly mirror `controller.db.backup` from its active sibling.
+
+If the active controller goes down, rename `controller.db.backup` to `controller.db` on the hot spare and start the ZeroTier One service there. The spare will take over and has now become the active controller. If the original active node comes back, it should take on the role of spare and should not start its service. Instead it should start mirroring the active controller's backup and wait until it is needed.
+
+The details of actually implementing this kind of HA fail-over on Linux or other OSes are beyond the scope of these docs and there are many ways to do it. Docker orchestration tools like Kubernetes can also be used to accomplish this if you've dockerized your controller.
+
+### 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.
+
+Also note that the API is *very* sensitive about types. Integers must be integers and strings strings, etc. Incorrectly typed and unrecognized fields are just ignored.
+
+#### `/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 2 | no |
+| clock | integer | Current clock on controller, ms since epoch | no |
+| instanceId | string | A random ID generated on first controller DB init | no |
+
+The instance ID can be used to check whether a controller's database has been reset or otherwise switched.
+
+#### `/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 |
+| --------------------- | ------------- | ------------------------------------------------- | -------- |
+| nwid | string | 16-digit network ID | no |
+| controllerInstanceId | string | Controller database instance ID | 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 | string | If 'zt', auto-assign IPv4 from pool(s) | YES |
+| v6AssignMode | string | IPv6 address auto-assign modes; 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 |
+| memberRevisionCounter | integer | Network member revision counter | no |
+| authorizedMemberCount | integer | Number of authorized members (for private nets) | no |
+| relays | array[object] | Alternative relays; see below | YES |
+| 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 |
+
+(The `ipLocalRoutes` field appeared in older versions but is no longer present. Routes will now show up in `routes`.)
+
+Two important things to know about networks:
+
+ - Networks without rules won't carry any traffic. See below for an example with rules to permit IPv4 and IPv6.
+ - 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 not common in ordinary use.
+
+**IPv6 Auto-Assign Modes:**
+
+This field is (for legacy reasons) a comma-delimited list of strings. These can be `rfc4193`, `6plane`, and `zt`. RFC4193 and 6PLANE are special addressing modes that deterministically assign IPv6 addresses based on the network ID and the ZeroTier address of each member. The `zt` mode enables IPv6 auto-assignment from arbitrary IPv6 IP ranges configured in `ipAssignmentPools`.
+
+**Relay object format:**
+
+Relay objects define network-specific preferred relay nodes. Traffic to peers on this network will preferentially use these relays if they are available, and otherwise will fall back to the global rootserver infrastructure.
+
+| Field | Type | Description | Writable |
+| --------------------- | ------------- | ------------------------------------------------- | -------- |
+| address | string | 10-digit ZeroTier address of relay | YES |
+| phyAddress | string | Optional IP/port suggestion for finding relay | YES |
+
+**IP assignment pool object format:**
+
+| Field | Type | Description | Writable |
+| --------------------- | ------------- | ------------------------------------------------- | -------- |
+| ipRangeStart | string | Starting IP address in range | YES |
+| ipRangeEnd | string | Ending IP address in range (inclusive) | YES |
+
+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:**
+
+Rules are matched in order of ruleNo. If no rules match, the default action is `drop`. To allow all traffic, create a single rule with all *null* fields and an action of `accept`.
+
+In the future there will be many, many more types of rules. As of today only filtering by Ethernet packet type is supported.
+
+| Field | Type | Description | Writable |
+| --------------------- | ------------- | ------------------------------------------------- | -------- |
+| ruleNo | integer | Rule sorting key | YES |
+| etherType | integer | Ethernet frame type (e.g. 34525 for IPv6) | YES |
+| action | string | Currently either `allow` or `drop` | YES |
+
+**An Example: The Configuration for Earth**
+
+Here is an example of a correctly configured ZeroTier network with IPv4 auto-assigned addresses from 28.0.0.0/7 (a "de-facto private" space) and RFC4193 IPv6 addressing. Users might recognize this as *Earth*, our public "global LAN party" that's used for demos and testing and occasionally gaming.
+
+For your own networks you'll probably want to change `private` to `true` unless you like company. These rules on the other hand probably are what you want. These allow IPv4, IPv4 ARP, and IPv6 Ethernet frames. To allow only IPv4 omit the one for Ethernet type 34525 (IPv6).
+
+ {
+ "nwid": "8056c2e21c000001",
+ "controllerInstanceId": "8ab354604debe1da27ee627c9ef94a48",
+ "clock": 1468004857100,
+ "name": "earth.zerotier.net",
+ "private": false,
+ "enableBroadcast": false,
+ "allowPassiveBridging": false,
+ "v4AssignMode": "zt",
+ "v6AssignMode": "rfc4193",
+ "multicastLimit": 64,
+ "creationTime": 1442292573165,
+ "revision": 234,
+ "memberRevisionCounter": 3326,
+ "authorizedMemberCount": 2873,
+ "relays": [],
+ "routes": [
+ {"target":"28.0.0.0/7","via":null,"flags":0,"metric":0}],
+ "ipAssignmentPools": [
+ {"ipRangeStart":"28.0.0.1","ipRangeEnd":"29.255.255.254"}],
+ "rules": [
+ {
+ "ruleNo": 20,
+ "etherType": 2048,
+ "action": "accept"
+ },{
+ "ruleNo": 21,
+ "etherType": 2054,
+ "action": "accept"
+ },{
+ "ruleNo": 30,
+ "etherType": 34525,
+ "action": "accept"
+ }]
+ }
+
+#### `/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.
-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:
+#### `/controller/network/<network ID>/active`
- sqlite3 /path/to/controller.db .dump
+ * Purpose: Get a set of all active members on this network
+ * Methods: GET
+ * Returns: { object }
-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 returns an object containing all currently online members and the most recent `recentLog` entries for their last request.
-### Administrating
+#### `/controller/network/<network ID>/member/<address>`
-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.
+ * Purpose: Create, authorize, or remove a network member
+ * Methods: GET, POST, DELETE
+ * Returns: { object }
-### Reliability
+| Field | Type | Description | Writable |
+| --------------------- | ------------- | ------------------------------------------------- | -------- |
+| nwid | string | 16-digit network ID | no |
+| clock | integer | Current clock, ms since epoch | no |
+| address | string | Member's 10-digit ZeroTier address | no |
+| authorized | boolean | Is member authorized? (for private networks) | YES |
+| 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 |
-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.
+Note that managed IP assignments are only used if they fall within a managed route. Otherwise they are ignored.
-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.
+**Recent log object format:**
-### Limits
+| 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 |
+| fromAddr | string | Physical address if known |
-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.
+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.
diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp
index 61ac0cd3..65051744 100644
--- a/controller/SqliteNetworkController.cpp
+++ b/controller/SqliteNetworkController.cpp
@@ -70,7 +70,7 @@
#define ZT_NETCONF_SQLITE_SCHEMA_VERSION_STR "4"
// API version reported via JSON control plane
-#define ZT_NETCONF_CONTROLLER_API_VERSION 1
+#define ZT_NETCONF_CONTROLLER_API_VERSION 2
// Number of requests to remember in member history
#define ZT_NETCONF_DB_MEMBER_HISTORY_LENGTH 8
@@ -85,12 +85,13 @@
#define ZT_NETCONF_NODE_ACTIVE_THRESHOLD ((ZT_NETWORK_AUTOCONF_DELAY * 2) + 5000)
// Flags for Network 'flags' field in table
-#define ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4 1
+#define ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4_AUTO_ASSIGN 1
#define ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_RFC4193 2
#define ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_6PLANE 4
+#define ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_AUTO_ASSIGN 8
// Flags with all V6 managed mode flags flipped off -- for masking in update operation and in string form for SQL building
-#define ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_MASK_S "268435449"
+#define ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_MASK_S "268435441"
// Uncomment to trace Sqlite for debugging
//#define ZT_NETCONF_SQLITE_TRACE 1
@@ -351,7 +352,7 @@ SqliteNetworkController::SqliteNetworkController(Node *node,const char *dbPath,c
if (
/* Network */
- (sqlite3_prepare_v2(_db,"SELECT name,private,enableBroadcast,allowPassiveBridging,\"flags\",multicastLimit,creationTime,revision,memberRevisionCounter FROM Network WHERE id = ?",-1,&_sGetNetworkById,(const char **)0) != SQLITE_OK)
+ (sqlite3_prepare_v2(_db,"SELECT name,private,enableBroadcast,allowPassiveBridging,\"flags\",multicastLimit,creationTime,revision,memberRevisionCounter,(SELECT COUNT(1) FROM Member WHERE Member.networkId = Network.id AND Member.authorized > 0) FROM Network WHERE id = ?",-1,&_sGetNetworkById,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT revision FROM Network WHERE id = ?",-1,&_sGetNetworkRevision,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"UPDATE Network SET revision = ? WHERE id = ?",-1,&_sSetNetworkRevision,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"INSERT INTO Network (id,name,creationTime,revision) VALUES (?,?,?,1)",-1,&_sCreateNetwork,(const char **)0) != SQLITE_OK)
@@ -376,8 +377,7 @@ SqliteNetworkController::SqliteNetworkController(Node *node,const char *dbPath,c
||(sqlite3_prepare_v2(_db,"DELETE FROM IpAssignmentPool WHERE networkId = ?",-1,&_sDeleteIpAssignmentPoolsForNetwork,(const char **)0) != SQLITE_OK)
/* IpAssignment */
- ||(sqlite3_prepare_v2(_db,"SELECT \"type\",ip,ipNetmaskBits FROM IpAssignment WHERE networkId = ? AND (nodeId = ? OR nodeId IS NULL) AND ipVersion = ?",-1,&_sGetIpAssignmentsForNode,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"SELECT ip,ipNetmaskBits,ipVersion FROM IpAssignment WHERE networkId = ? AND nodeId = ? AND \"type\" = ? ORDER BY ip ASC",-1,&_sGetIpAssignmentsForNode2,(const char **)0) != SQLITE_OK)
+ ||(sqlite3_prepare_v2(_db,"SELECT ip,ipNetmaskBits,ipVersion FROM IpAssignment WHERE networkId = ? AND nodeId = ? AND \"type\" = 0 ORDER BY ip ASC",-1,&_sGetIpAssignmentsForNode,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT 1 FROM IpAssignment WHERE networkId = ? AND ip = ? AND ipVersion = ? AND \"type\" = ?",-1,&_sCheckIfIpIsAllocated,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"INSERT INTO IpAssignment (networkId,nodeId,\"type\",ip,ipNetmaskBits,ipVersion) VALUES (?,?,?,?,?,?)",-1,&_sAllocateIp,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"DELETE FROM IpAssignment WHERE networkId = ? AND nodeId = ? AND \"type\" = ?",-1,&_sDeleteIpAllocations,(const char **)0) != SQLITE_OK)
@@ -474,7 +474,6 @@ SqliteNetworkController::~SqliteNetworkController()
sqlite3_finalize(_sCreateNetwork);
sqlite3_finalize(_sGetNetworkRevision);
sqlite3_finalize(_sSetNetworkRevision);
- sqlite3_finalize(_sGetIpAssignmentsForNode2);
sqlite3_finalize(_sDeleteRelaysForNetwork);
sqlite3_finalize(_sCreateRelay);
sqlite3_finalize(_sDeleteIpAssignmentPoolsForNetwork);
@@ -740,6 +739,7 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
++nc.routeCount;
}
+ // Assign special IPv6 addresses if these are enabled
if (((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_RFC4193) != 0)&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
nc.staticIps[nc.staticIpCount++] = InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt());
nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
@@ -749,99 +749,188 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
}
- if ((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4) != 0) {
- bool haveStaticIpAssignment = false;
+ // Get managed addresses that are assigned to this member
+ bool haveManagedIpv4AutoAssignment = false;
+ bool haveManagedIpv6AutoAssignment = false; // "special" NDP-emulated address types do not count
+ sqlite3_reset(_sGetIpAssignmentsForNode);
+ sqlite3_bind_text(_sGetIpAssignmentsForNode,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_text(_sGetIpAssignmentsForNode,2,member.nodeId,10,SQLITE_STATIC);
+ while (sqlite3_step(_sGetIpAssignmentsForNode) == SQLITE_ROW) {
+ const unsigned char *const ipbytes = (const unsigned char *)sqlite3_column_blob(_sGetIpAssignmentsForNode,0);
+ if ((!ipbytes)||(sqlite3_column_bytes(_sGetIpAssignmentsForNode,0) != 16))
+ continue;
+ //const int ipNetmaskBits = sqlite3_column_int(_sGetIpAssignmentsForNode,1);
+ const int ipVersion = sqlite3_column_int(_sGetIpAssignmentsForNode,2);
+
+ InetAddress ip;
+ if (ipVersion == 4)
+ ip = InetAddress(ipbytes + 12,4,0);
+ else if (ipVersion == 6)
+ ip = InetAddress(ipbytes,16,0);
+ else continue;
+
+ // IP assignments are only pushed if there is a corresponding local route. We also now get the netmask bits from
+ // this route, ignoring the netmask bits field of the assigned IP itself. Using that was worthless and a source
+ // of user error / poor UX.
+ int routedNetmaskBits = 0;
+ for(unsigned int rk=0;rk<nc.routeCount;++rk) {
+ if ( (!nc.routes[rk].via.ss_family) && (reinterpret_cast<const InetAddress *>(&(nc.routes[rk].target))->containsAddress(ip)) )
+ routedNetmaskBits = reinterpret_cast<const InetAddress *>(&(nc.routes[rk].target))->netmaskBits();
+ }
- sqlite3_reset(_sGetIpAssignmentsForNode);
- sqlite3_bind_text(_sGetIpAssignmentsForNode,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_text(_sGetIpAssignmentsForNode,2,member.nodeId,10,SQLITE_STATIC);
- sqlite3_bind_int(_sGetIpAssignmentsForNode,3,4); // 4 == IPv4
- while (sqlite3_step(_sGetIpAssignmentsForNode) == SQLITE_ROW) {
- const unsigned char *const ip = (const unsigned char *)sqlite3_column_blob(_sGetIpAssignmentsForNode,1);
- if ((!ip)||(sqlite3_column_bytes(_sGetIpAssignmentsForNode,1) != 16))
- continue;
- int ipNetmaskBits = sqlite3_column_int(_sGetIpAssignmentsForNode,2);
- if ((ipNetmaskBits <= 0)||(ipNetmaskBits > 32))
+ if (routedNetmaskBits > 0) {
+ if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
+ ip.setPort(routedNetmaskBits);
+ nc.staticIps[nc.staticIpCount++] = ip;
+ }
+ if (ipVersion == 4)
+ haveManagedIpv4AutoAssignment = true;
+ else if (ipVersion == 6)
+ haveManagedIpv6AutoAssignment = true;
+ }
+ }
+
+ // Auto-assign IPv6 address if auto-assignment is enabled and it's needed
+ if ( ((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_AUTO_ASSIGN) != 0) && (!haveManagedIpv6AutoAssignment) && (!amActiveBridge) ) {
+ sqlite3_reset(_sGetIpAssignmentPools);
+ sqlite3_bind_text(_sGetIpAssignmentPools,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_int(_sGetIpAssignmentPools,2,6); // 6 == IPv6
+ while (sqlite3_step(_sGetIpAssignmentPools) == SQLITE_ROW) {
+ const uint8_t *const ipRangeStartB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,0));
+ const uint8_t *const ipRangeEndB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,1));
+ if ((!ipRangeStartB)||(!ipRangeEndB)||(sqlite3_column_bytes(_sGetIpAssignmentPools,0) != 16)||(sqlite3_column_bytes(_sGetIpAssignmentPools,1) != 16))
continue;
- if (sqlite3_column_int(_sGetIpAssignmentsForNode,0) == 0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/) {
- if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
- struct sockaddr_in *const v4ip = reinterpret_cast<struct sockaddr_in *>(&(nc.staticIps[nc.staticIpCount++]));
- v4ip->sin_family = AF_INET;
- v4ip->sin_port = Utils::hton((uint16_t)ipNetmaskBits);
- memcpy(&(v4ip->sin_addr.s_addr),ip + 12,4);
+
+ uint64_t s[2],e[2],x[2],xx[2];
+ memcpy(s,ipRangeStartB,16);
+ memcpy(e,ipRangeEndB,16);
+ s[0] = Utils::ntoh(s[0]);
+ s[1] = Utils::ntoh(s[1]);
+ e[0] = Utils::ntoh(e[0]);
+ e[1] = Utils::ntoh(e[1]);
+ x[0] = s[0];
+ x[1] = s[1];
+
+ for(unsigned int trialCount=0;trialCount<1000;++trialCount) {
+ if ((trialCount == 0)&&(e[1] > s[1])&&((e[1] - s[1]) >= 0xffffffffffULL)) {
+ // First see if we can just cram a ZeroTier ID into the higher 64 bits. If so do that.
+ xx[0] = Utils::hton(x[0]);
+ xx[1] = Utils::hton(x[1] + identity.address().toInt());
+ } else {
+ // Otherwise pick random addresses -- this technically doesn't explore the whole range if the lower 64 bit range is >= 1 but that won't matter since that would be huge anyway
+ Utils::getSecureRandom((void *)xx,16);
+ if ((e[0] > s[0]))
+ xx[0] %= (e[0] - s[0]);
+ else xx[0] = 0;
+ if ((e[1] > s[1]))
+ xx[1] %= (e[1] - s[1]);
+ else xx[1] = 0;
+ xx[0] = Utils::hton(x[0] + xx[0]);
+ xx[1] = Utils::hton(x[1] + xx[1]);
+ }
+
+ InetAddress ip6((const void *)xx,16,0);
+
+ // Check if this IP is within a local-to-Ethernet routed network
+ int routedNetmaskBits = 0;
+ for(unsigned int rk=0;rk<nc.routeCount;++rk) {
+ if ( (!nc.routes[rk].via.ss_family) && (nc.routes[rk].target.ss_family == AF_INET6) && (reinterpret_cast<const InetAddress *>(&(nc.routes[rk].target))->containsAddress(ip6)) )
+ routedNetmaskBits = reinterpret_cast<const InetAddress *>(&(nc.routes[rk].target))->netmaskBits();
+ }
+
+ // If it's routed, then try to claim and assign it and if successful end loop
+ if (routedNetmaskBits > 0) {
+ sqlite3_reset(_sCheckIfIpIsAllocated);
+ sqlite3_bind_text(_sCheckIfIpIsAllocated,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_blob(_sCheckIfIpIsAllocated,2,(const void *)ip6.rawIpData(),16,SQLITE_STATIC);
+ sqlite3_bind_int(_sCheckIfIpIsAllocated,3,6); // 6 == IPv6
+ sqlite3_bind_int(_sCheckIfIpIsAllocated,4,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
+ if (sqlite3_step(_sCheckIfIpIsAllocated) != SQLITE_ROW) {
+ // No rows returned, so the IP is available
+ sqlite3_reset(_sAllocateIp);
+ sqlite3_bind_text(_sAllocateIp,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_text(_sAllocateIp,2,member.nodeId,10,SQLITE_STATIC);
+ sqlite3_bind_int(_sAllocateIp,3,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
+ sqlite3_bind_blob(_sAllocateIp,4,(const void *)ip6.rawIpData(),16,SQLITE_STATIC);
+ sqlite3_bind_int(_sAllocateIp,5,routedNetmaskBits); // IP netmask bits from matching route
+ sqlite3_bind_int(_sAllocateIp,6,6); // 6 == IPv6
+ if (sqlite3_step(_sAllocateIp) == SQLITE_DONE) {
+ ip6.setPort(routedNetmaskBits);
+ if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)
+ nc.staticIps[nc.staticIpCount++] = ip6;
+ break;
+ }
+ }
}
- haveStaticIpAssignment = true;
}
}
+ }
- if ((!haveStaticIpAssignment)&&(!amActiveBridge)) {
- // Attempt to auto-assign an IPv4 address from an available routed pool if there is one
- sqlite3_reset(_sGetIpAssignmentPools);
- sqlite3_bind_text(_sGetIpAssignmentPools,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_int(_sGetIpAssignmentPools,2,4); // 4 == IPv4
-
- while (sqlite3_step(_sGetIpAssignmentPools) == SQLITE_ROW) {
- const unsigned char *ipRangeStartB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,0));
- const unsigned char *ipRangeEndB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,1));
- if ((!ipRangeStartB)||(!ipRangeEndB)||(sqlite3_column_bytes(_sGetIpAssignmentPools,0) != 16)||(sqlite3_column_bytes(_sGetIpAssignmentPools,1) != 16))
- continue;
+ // Auto-assign IPv4 address if auto-assignment is enabled and it's needed
+ if ( ((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4_AUTO_ASSIGN) != 0) && (!haveManagedIpv4AutoAssignment) && (!amActiveBridge) ) {
+ sqlite3_reset(_sGetIpAssignmentPools);
+ sqlite3_bind_text(_sGetIpAssignmentPools,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_int(_sGetIpAssignmentPools,2,4); // 4 == IPv4
+ while (sqlite3_step(_sGetIpAssignmentPools) == SQLITE_ROW) {
+ const unsigned char *ipRangeStartB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,0));
+ const unsigned char *ipRangeEndB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,1));
+ if ((!ipRangeStartB)||(!ipRangeEndB)||(sqlite3_column_bytes(_sGetIpAssignmentPools,0) != 16)||(sqlite3_column_bytes(_sGetIpAssignmentPools,1) != 16))
+ continue;
- uint32_t ipRangeStart = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeStartB + 12)));
- uint32_t ipRangeEnd = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeEndB + 12)));
- if ((ipRangeEnd <= ipRangeStart)||(ipRangeStart == 0))
- continue;
- uint32_t ipRangeLen = ipRangeEnd - ipRangeStart;
-
- // Start with the LSB of the member's address
- uint32_t ipTrialCounter = (uint32_t)(identity.address().toInt() & 0xffffffff);
-
- for(uint32_t k=ipRangeStart,l=0;(k<=ipRangeEnd)&&(l < 1000000);++k,++l) {
- uint32_t ip = (ipRangeLen > 0) ? (ipRangeStart + (ipTrialCounter % ipRangeLen)) : ipRangeStart;
- ++ipTrialCounter;
- if ((ip & 0x000000ff) == 0x000000ff)
- continue; // don't allow addresses that end in .255
-
- // Check if this IPv4 IP is within a local-to-Ethernet routed network
- int routedNetmaskBits = 0;
- for(unsigned int rk=0;rk<nc.routeCount;++rk) {
- if ((!nc.routes[rk].via.ss_family)&&(nc.routes[rk].target.ss_family == AF_INET)) {
- uint32_t targetIp = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc.routes[rk].target))->sin_addr.s_addr));
- int targetBits = Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc.routes[rk].target))->sin_port));
- if ((ip & (0xffffffff << (32 - targetBits))) == targetIp) {
- routedNetmaskBits = targetBits;
- break;
- }
+ uint32_t ipRangeStart = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeStartB + 12)));
+ uint32_t ipRangeEnd = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeEndB + 12)));
+ if ((ipRangeEnd <= ipRangeStart)||(ipRangeStart == 0))
+ continue;
+ uint32_t ipRangeLen = ipRangeEnd - ipRangeStart;
+
+ // Start with the LSB of the member's address
+ uint32_t ipTrialCounter = (uint32_t)(identity.address().toInt() & 0xffffffff);
+
+ for(uint32_t k=ipRangeStart,trialCount=0;(k<=ipRangeEnd)&&(trialCount < 1000);++k,++trialCount) {
+ uint32_t ip = (ipRangeLen > 0) ? (ipRangeStart + (ipTrialCounter % ipRangeLen)) : ipRangeStart;
+ ++ipTrialCounter;
+ if ((ip & 0x000000ff) == 0x000000ff)
+ continue; // don't allow addresses that end in .255
+
+ // Check if this IP is within a local-to-Ethernet routed network
+ int routedNetmaskBits = 0;
+ for(unsigned int rk=0;rk<nc.routeCount;++rk) {
+ if ((!nc.routes[rk].via.ss_family)&&(nc.routes[rk].target.ss_family == AF_INET)) {
+ uint32_t targetIp = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc.routes[rk].target))->sin_addr.s_addr));
+ int targetBits = Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc.routes[rk].target))->sin_port));
+ if ((ip & (0xffffffff << (32 - targetBits))) == targetIp) {
+ routedNetmaskBits = targetBits;
+ break;
}
}
+ }
- // If it's routed, then try to claim and assign it and if successful end loop
- if (routedNetmaskBits > 0) {
- uint32_t ipBlob[4]; // actually a 16-byte blob, we put IPv4s in the last 4 bytes
- ipBlob[0] = 0; ipBlob[1] = 0; ipBlob[2] = 0; ipBlob[3] = Utils::hton(ip);
- sqlite3_reset(_sCheckIfIpIsAllocated);
- sqlite3_bind_text(_sCheckIfIpIsAllocated,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_blob(_sCheckIfIpIsAllocated,2,(const void *)ipBlob,16,SQLITE_STATIC);
- sqlite3_bind_int(_sCheckIfIpIsAllocated,3,4); // 4 == IPv4
- sqlite3_bind_int(_sCheckIfIpIsAllocated,4,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
- if (sqlite3_step(_sCheckIfIpIsAllocated) != SQLITE_ROW) {
- // No rows returned, so the IP is available
- sqlite3_reset(_sAllocateIp);
- sqlite3_bind_text(_sAllocateIp,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_text(_sAllocateIp,2,member.nodeId,10,SQLITE_STATIC);
- sqlite3_bind_int(_sAllocateIp,3,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
- sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC);
- sqlite3_bind_int(_sAllocateIp,5,routedNetmaskBits); // IP netmask bits from matching route
- sqlite3_bind_int(_sAllocateIp,6,4); // 4 == IPv4
- if (sqlite3_step(_sAllocateIp) == SQLITE_DONE) {
- if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
- struct sockaddr_in *const v4ip = reinterpret_cast<struct sockaddr_in *>(&(nc.staticIps[nc.staticIpCount++]));
- v4ip->sin_family = AF_INET;
- v4ip->sin_port = Utils::hton((uint16_t)routedNetmaskBits);
- v4ip->sin_addr.s_addr = Utils::hton(ip);
- }
- haveStaticIpAssignment = true;
- break;
+ // If it's routed, then try to claim and assign it and if successful end loop
+ if (routedNetmaskBits > 0) {
+ uint32_t ipBlob[4]; // actually a 16-byte blob, we put IPv4s in the last 4 bytes
+ ipBlob[0] = 0; ipBlob[1] = 0; ipBlob[2] = 0; ipBlob[3] = Utils::hton(ip);
+ sqlite3_reset(_sCheckIfIpIsAllocated);
+ sqlite3_bind_text(_sCheckIfIpIsAllocated,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_blob(_sCheckIfIpIsAllocated,2,(const void *)ipBlob,16,SQLITE_STATIC);
+ sqlite3_bind_int(_sCheckIfIpIsAllocated,3,4); // 4 == IPv4
+ sqlite3_bind_int(_sCheckIfIpIsAllocated,4,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
+ if (sqlite3_step(_sCheckIfIpIsAllocated) != SQLITE_ROW) {
+ // No rows returned, so the IP is available
+ sqlite3_reset(_sAllocateIp);
+ sqlite3_bind_text(_sAllocateIp,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_text(_sAllocateIp,2,member.nodeId,10,SQLITE_STATIC);
+ sqlite3_bind_int(_sAllocateIp,3,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
+ sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC);
+ sqlite3_bind_int(_sAllocateIp,5,routedNetmaskBits); // IP netmask bits from matching route
+ sqlite3_bind_int(_sAllocateIp,6,4); // 4 == IPv4
+ if (sqlite3_step(_sAllocateIp) == SQLITE_DONE) {
+ if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
+ struct sockaddr_in *const v4ip = reinterpret_cast<struct sockaddr_in *>(&(nc.staticIps[nc.staticIpCount++]));
+ v4ip->sin_family = AF_INET;
+ v4ip->sin_port = Utils::hton((uint16_t)routedNetmaskBits);
+ v4ip->sin_addr.s_addr = Utils::hton(ip);
}
+ break;
}
}
}
@@ -997,7 +1086,7 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
sqlite3_bind_text(_sAllocateIp,2,addrs,10,SQLITE_STATIC);
sqlite3_bind_int(_sAllocateIp,3,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC);
- sqlite3_bind_int(_sAllocateIp,5,(int)a.netmaskBits());
+ sqlite3_bind_int(_sAllocateIp,5,(int)a.netmaskBits()); // NOTE: this field is now ignored but set it anyway
sqlite3_bind_int(_sAllocateIp,6,ipVersion);
if (sqlite3_step(_sAllocateIp) != SQLITE_DONE)
return 500;
@@ -1181,10 +1270,10 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
} else if (!strcmp(j->u.object.values[k].name,"v4AssignMode")) {
if ((j->u.object.values[k].value->type == json_string)&&(!strcmp(j->u.object.values[k].value->u.string.ptr,"zt"))) {
if (sqlite3_prepare_v2(_db,"UPDATE Network SET \"flags\" = (\"flags\" | ?) WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
- sqlite3_bind_int(stmt,1,(int)ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4);
+ sqlite3_bind_int(stmt,1,(int)ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4_AUTO_ASSIGN);
} else {
if (sqlite3_prepare_v2(_db,"UPDATE Network SET \"flags\" = (\"flags\" & ?) WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
- sqlite3_bind_int(stmt,1,(int)(ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4 ^ 0xfffffff));
+ sqlite3_bind_int(stmt,1,(int)(ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4_AUTO_ASSIGN ^ 0xfffffff));
}
} else if (!strcmp(j->u.object.values[k].name,"v6AssignMode")) {
int fl = 0;
@@ -1195,6 +1284,8 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
fl |= ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_RFC4193;
else if (!strcmp(f,"6plane"))
fl |= ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_6PLANE;
+ else if (!strcmp(f,"zt"))
+ fl |= ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_AUTO_ASSIGN;
}
}
if (sqlite3_prepare_v2(_db,"UPDATE Network SET \"flags\" = ((\"flags\" & " ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_MASK_S ") | ?) WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
@@ -1680,22 +1771,21 @@ unsigned int SqliteNetworkController::_doCPGet(
_jsonEscape(memberIdStr).c_str());
responseBody = json;
- sqlite3_reset(_sGetIpAssignmentsForNode2);
- sqlite3_bind_text(_sGetIpAssignmentsForNode2,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_text(_sGetIpAssignmentsForNode2,2,addrs,10,SQLITE_STATIC);
- sqlite3_bind_int(_sGetIpAssignmentsForNode2,3,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
+ sqlite3_reset(_sGetIpAssignmentsForNode);
+ sqlite3_bind_text(_sGetIpAssignmentsForNode,1,nwids,16,SQLITE_STATIC);
+ sqlite3_bind_text(_sGetIpAssignmentsForNode,2,addrs,10,SQLITE_STATIC);
bool firstIp = true;
- while (sqlite3_step(_sGetIpAssignmentsForNode2) == SQLITE_ROW) {
- int ipversion = sqlite3_column_int(_sGetIpAssignmentsForNode2,2);
+ while (sqlite3_step(_sGetIpAssignmentsForNode) == SQLITE_ROW) {
+ int ipversion = sqlite3_column_int(_sGetIpAssignmentsForNode,2);
char ipBlob[16];
- memcpy(ipBlob,(const void *)sqlite3_column_blob(_sGetIpAssignmentsForNode2,0),16);
+ memcpy(ipBlob,(const void *)sqlite3_column_blob(_sGetIpAssignmentsForNode,0),16);
InetAddress ip(
(const void *)(ipversion == 6 ? ipBlob : &ipBlob[12]),
(ipversion == 6 ? 16 : 4),
- (unsigned int)sqlite3_column_int(_sGetIpAssignmentsForNode2,1)
+ (unsigned int)sqlite3_column_int(_sGetIpAssignmentsForNode,1)
);
responseBody.append(firstIp ? "\"" : ",\"");
- responseBody.append(_jsonEscape(ip.toString()));
+ responseBody.append(_jsonEscape(ip.toIpString()));
responseBody.push_back('"');
firstIp = false;
}
@@ -1801,8 +1891,11 @@ unsigned int SqliteNetworkController::_doCPGet(
v6modes.push_back(',');
v6modes.append("6plane");
}
- if (v6modes.length() == 0)
- v6modes = "none";
+ if ((fl & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_AUTO_ASSIGN) != 0) {
+ if (v6modes.length() > 0)
+ v6modes.push_back(',');
+ v6modes.append("zt");
+ }
Utils::snprintf(json,sizeof(json),
"{\n"
@@ -1828,13 +1921,13 @@ unsigned int SqliteNetworkController::_doCPGet(
(sqlite3_column_int(_sGetNetworkById,1) > 0) ? "true" : "false",
(sqlite3_column_int(_sGetNetworkById,2) > 0) ? "true" : "false",
(sqlite3_column_int(_sGetNetworkById,3) > 0) ? "true" : "false",
- (((fl & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4) != 0) ? "zt" : "none"),
+ (((fl & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4_AUTO_ASSIGN) != 0) ? "zt" : ""),
v6modes.c_str(),
sqlite3_column_int(_sGetNetworkById,5),
(unsigned long long)sqlite3_column_int64(_sGetNetworkById,6),
(unsigned long long)sqlite3_column_int64(_sGetNetworkById,7),
(unsigned long long)sqlite3_column_int64(_sGetNetworkById,8),
- (unsigned long long)0); // TODO: authorized member count, but more efficiently
+ (unsigned long long)sqlite3_column_int64(_sGetNetworkById,9));
responseBody = json;
sqlite3_reset(_sGetRelays);
diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp
index efeb63b1..145788c7 100644
--- a/controller/SqliteNetworkController.hpp
+++ b/controller/SqliteNetworkController.hpp
@@ -154,7 +154,6 @@ private:
sqlite3_stmt *_sCreateNetwork;
sqlite3_stmt *_sGetNetworkRevision;
sqlite3_stmt *_sSetNetworkRevision;
- sqlite3_stmt *_sGetIpAssignmentsForNode2;
sqlite3_stmt *_sDeleteRelaysForNetwork;
sqlite3_stmt *_sCreateRelay;
sqlite3_stmt *_sDeleteIpAssignmentPoolsForNetwork;
diff --git a/debian/changelog b/debian/changelog
index c6e9eb90..3b33c1e2 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,22 @@
+zerotier-one (1.1.12) unstable; urgency=medium
+
+ * See https://github.com/zerotier/ZeroTierOne for release notes.
+
+ -- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 12 Jul 2016 03:02:22 -0700
+
+zerotier-one (1.1.10) unstable; urgency=medium
+
+ * See https://github.com/zerotier/ZeroTierOne for release notes.
+ * ZeroTier Debian packages no longer depend on http-parser since its ABI is too unstable.
+
+ -- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 12 Jul 2016 12:29:00 -0700
+
+zerotier-one (1.1.8) unstable; urgency=low
+
+ * See https://github.com/zerotier/ZeroTierOne for release notes.
+
+ -- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 08 Jul 2016 01:56:00 -0700
+
zerotier-one (1.1.6) unstable; urgency=medium
* First Debian release on ZeroTier, Inc. private apt repository.
diff --git a/debian/control b/debian/control
index cfe50682..46b8307f 100644
--- a/debian/control
+++ b/debian/control
@@ -3,14 +3,14 @@ Maintainer: Adam Ierymenko <adam.ierymenko@zerotier.com>
Section: net
Priority: optional
Standards-Version: 3.9.6
-Build-Depends: debhelper (>= 9), libhttp-parser-dev (>= 2.1), liblz4-dev, libnatpmp-dev, dh-systemd, ruby-ronn
+Build-Depends: debhelper (>= 9), liblz4-dev, libnatpmp-dev, dh-systemd, ruby-ronn
Vcs-Git: git://github.com/zerotier/ZeroTierOne
Vcs-Browser: https://github.com/zerotier/ZeroTierOne
Homepage: https://www.zerotier.com/
Package: zerotier-one
Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, libhttp-parser2.1, liblz4-1, libnatpmp1, iproute2
+Depends: ${shlibs:Depends}, ${misc:Depends}, liblz4-1, libnatpmp1, iproute2
Homepage: https://www.zerotier.com/
Description: ZeroTier network virtualization service
ZeroTier One lets you join ZeroTier virtual networks and
diff --git a/debian/control.wheezy b/debian/control.wheezy
new file mode 100644
index 00000000..0cbd151b
--- /dev/null
+++ b/debian/control.wheezy
@@ -0,0 +1,19 @@
+Source: zerotier-one
+Maintainer: Adam Ierymenko <adam.ierymenko@zerotier.com>
+Section: net
+Priority: optional
+Standards-Version: 3.9.4
+Build-Depends: debhelper (>= 9), ruby-ronn
+Vcs-Git: git://github.com/zerotier/ZeroTierOne
+Vcs-Browser: https://github.com/zerotier/ZeroTierOne
+Homepage: https://www.zerotier.com/
+
+Package: zerotier-one
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}, iproute
+Homepage: https://www.zerotier.com/
+Description: ZeroTier network virtualization service
+ ZeroTier One lets you join ZeroTier virtual networks and
+ have them appear as tun/tap ports on your system. See
+ https://www.zerotier.com/ for instructions and
+ documentation.
diff --git a/debian/rules.wheezy b/debian/rules.wheezy
new file mode 100755
index 00000000..e51d794e
--- /dev/null
+++ b/debian/rules.wheezy
@@ -0,0 +1,11 @@
+#!/usr/bin/make -f
+
+CFLAGS=-O3 -fstack-protector
+CXXFLAGS=-O3 -fstack-protector
+
+%:
+ dh $@
+
+override_dh_auto_build:
+ make ZT_USE_MINIUPNPC=1 -j 2
+
diff --git a/ext/installfiles/mac/ZeroTier One.pkgproj b/ext/installfiles/mac/ZeroTier One.pkgproj
index e090c574..acbc3cfb 100755
--- a/ext/installfiles/mac/ZeroTier One.pkgproj
+++ b/ext/installfiles/mac/ZeroTier One.pkgproj
@@ -759,7 +759,7 @@
<key>OVERWRITE_PERMISSIONS</key>
<false/>
<key>VERSION</key>
- <string>1.1.6</string>
+ <string>1.1.12</string>
</dict>
<key>PROJECT_COMMENTS</key>
<dict>
diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h
index d46c64b8..2d7b007b 100644
--- a/include/ZeroTierOne.h
+++ b/include/ZeroTierOne.h
@@ -117,6 +117,11 @@ extern "C" {
#define ZT_MAX_PEER_NETWORK_PATHS 4
/**
+ * Maximum number of trusted physical network paths
+ */
+#define ZT_MAX_TRUSTED_PATHS 16
+
+/**
* Maximum number of hops in a ZeroTier circuit test
*
* This is more or less the max that can be fit in a given packet (with
@@ -888,6 +893,11 @@ typedef struct
uint64_t lastReceive;
/**
+ * Is this a trusted path? If so this will be its nonzero ID.
+ */
+ uint64_t trustedPathId;
+
+ /**
* Is path active?
*/
int active;
@@ -1838,6 +1848,29 @@ void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned
void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs);
/**
+ * Set trusted paths
+ *
+ * A trusted path is a physical network (network/bits) over which both
+ * encryption and authentication can be skipped to improve performance.
+ * Each trusted path must have a non-zero unique ID that is the same across
+ * all participating nodes.
+ *
+ * We don't recommend using trusted paths at all unless you really *need*
+ * near-bare-metal performance. Even on a LAN authentication and encryption
+ * 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
+ * all trusted paths.
+ *
+ * @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);
+
+/**
* Do things in the background until Node dies
*
* This function can be called from one or more background threads to process
diff --git a/java/jni/Android.mk b/java/jni/Android.mk
index 5c2f1c79..c563879c 100644
--- a/java/jni/Android.mk
+++ b/java/jni/Android.mk
@@ -16,7 +16,6 @@ LOCAL_SRC_FILES := \
$(ZT1)/node/C25519.cpp \
$(ZT1)/node/CertificateOfMembership.cpp \
$(ZT1)/node/DeferredPackets.cpp \
- $(ZT1)/node/Dictionary.cpp \
$(ZT1)/node/Identity.cpp \
$(ZT1)/node/IncomingPacket.cpp \
$(ZT1)/node/InetAddress.cpp \
diff --git a/java/jni/ZT_jniutils.cpp b/java/jni/ZT_jniutils.cpp
index ae1aa929..512bf839 100644
--- a/java/jni/ZT_jniutils.cpp
+++ b/java/jni/ZT_jniutils.cpp
@@ -363,51 +363,6 @@ jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr)
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)
{
LOGV("newPeerPhysicalPath Called");
@@ -652,9 +607,7 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
jfieldID bridgeField = NULL;
jfieldID broadcastEnabledField = NULL;
jfieldID portErrorField = NULL;
- jfieldID enabledField = NULL;
jfieldID netconfRevisionField = NULL;
- jfieldID multicastSubscriptionsField = NULL;
jfieldID assignedAddressesField = NULL;
vnetConfigClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkConfig");
@@ -749,13 +702,6 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
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");
if(env->ExceptionCheck() || netconfRevisionField == NULL)
{
@@ -763,13 +709,6 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
return NULL;
}
- multicastSubscriptionsField = lookup.findField(vnetConfigClass, "multicastSubscriptions", "[Lcom/zerotier/sdk/MulticastGroup;");
- if(env->ExceptionCheck() || multicastSubscriptionsField == NULL)
- {
- LOGE("Error getting multicastSubscriptions field");
- return NULL;
- }
-
assignedAddressesField = lookup.findField(vnetConfigClass, "assignedAddresses", "[Ljava/net/InetSocketAddress;");
if(env->ExceptionCheck() || assignedAddressesField == NULL)
{
@@ -804,34 +743,8 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
env->SetBooleanField(vnetConfigObj, dhcpField, vnetConfig.dhcp);
env->SetBooleanField(vnetConfigObj, bridgeField, vnetConfig.bridge);
env->SetBooleanField(vnetConfigObj, broadcastEnabledField, vnetConfig.broadcastEnabled);
- env->SetBooleanField(vnetConfigObj, enabledField, vnetConfig.enabled);
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");
if(env->ExceptionCheck() || inetSocketAddressClass == NULL)
{
@@ -863,7 +776,7 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
return vnetConfigObj;
}
-jobject newVersion(JNIEnv *env, int major, int minor, int rev, long featureFlags)
+jobject newVersion(JNIEnv *env, int major, int minor, int rev)
{
// create a com.zerotier.sdk.Version object
jclass versionClass = NULL;
@@ -892,7 +805,6 @@ jobject newVersion(JNIEnv *env, int major, int minor, int rev, long featureFlags
jfieldID majorField = NULL;
jfieldID minorField = NULL;
jfieldID revisionField = NULL;
- jfieldID featureFlagsField = NULL;
majorField = lookup.findField(versionClass, "major", "I");
if(env->ExceptionCheck() || majorField == NULL)
@@ -912,16 +824,9 @@ jobject newVersion(JNIEnv *env, int major, int minor, int rev, long featureFlags
return NULL;
}
- featureFlagsField = lookup.findField(versionClass, "featureFlags", "J");
- if(env->ExceptionCheck() || featureFlagsField == NULL)
- {
- return NULL;
- }
-
env->SetIntField(versionObj, majorField, (jint)major);
env->SetIntField(versionObj, minorField, (jint)minor);
env->SetIntField(versionObj, revisionField, (jint)rev);
- env->SetLongField(versionObj, featureFlagsField, (jlong)featureFlags);
return versionObj;
}
diff --git a/java/jni/ZT_jniutils.h b/java/jni/ZT_jniutils.h
index b76a28c2..34dfc471 100644
--- a/java/jni/ZT_jniutils.h
+++ b/java/jni/ZT_jniutils.h
@@ -40,7 +40,7 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp);
jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &config);
-jobject newVersion(JNIEnv *env, int major, int minor, int rev, long featureFlags);
+jobject newVersion(JNIEnv *env, int major, int minor, int rev);
#ifdef __cplusplus
}
diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp
index dbabf803..4d9a2102 100644
--- a/java/jni/com_zerotierone_sdk_Node.cpp
+++ b/java/jni/com_zerotierone_sdk_Node.cpp
@@ -1234,11 +1234,10 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_version(
int major = 0;
int minor = 0;
int revision = 0;
- unsigned long featureFlags = 0;
- ZT_version(&major, &minor, &revision, &featureFlags);
+ ZT_version(&major, &minor, &revision);
- return newVersion(env, major, minor, revision, featureFlags);
+ return newVersion(env, major, minor, revision);
}
/*
diff --git a/java/src/com/zerotier/sdk/MulticastGroup.java b/java/src/com/zerotier/sdk/MulticastGroup.java
deleted file mode 100644
index 68114424..00000000
--- a/java/src/com/zerotier/sdk/MulticastGroup.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2015 ZeroTier, Inc.
- *
- * 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/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-package com.zerotier.sdk;
-
-
-public final class MulticastGroup {
- private MulticastGroup() {}
-
- private long mac;
- private long adi;
-
- public boolean equals(MulticastGroup other) {
- return mac == other.mac && adi == other.adi;
- }
-
- /**
- * MAC address (least significant 48 bits)
- */
- public final long getMacAddress() {
- return mac;
- }
-
- /**
- * Additional distinguishing information (usually zero)
- */
- public final long getAdi() {
- return adi;
- }
-}
diff --git a/java/src/com/zerotier/sdk/Version.java b/java/src/com/zerotier/sdk/Version.java
index d7fa0ce4..c93c2597 100644
--- a/java/src/com/zerotier/sdk/Version.java
+++ b/java/src/com/zerotier/sdk/Version.java
@@ -33,5 +33,4 @@ public final class Version {
public int major = 0;
public int minor = 0;
public int revision = 0;
- public long featureFlags = 0;
} \ No newline at end of file
diff --git a/java/src/com/zerotier/sdk/VirtualNetworkConfig.java b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java
index 9816180b..fbcbd3a4 100644
--- a/java/src/com/zerotier/sdk/VirtualNetworkConfig.java
+++ b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java
@@ -49,7 +49,6 @@ public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConf
private int portError;
private boolean enabled;
private long netconfRevision;
- private MulticastGroup[] multicastSubscriptions;
private InetSocketAddress[] assignedAddresses;
private VirtualNetworkConfig() {
@@ -168,13 +167,6 @@ public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConf
}
/**
- * 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
*
* <p>If this is zero, it means we're still waiting for our netconf.</p>
@@ -184,13 +176,6 @@ public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConf
}
/**
- * Multicast group subscriptions
- */
- public final MulticastGroup[] multicastSubscriptions() {
- return multicastSubscriptions;
- }
-
- /**
* ZeroTier-assigned addresses (in {@link java.net.InetSocketAddress} objects)
*
* For IP, the port number of the sockaddr_XX structure contains the number
diff --git a/linux-build-farm/README.md b/linux-build-farm/README.md
index c55e7300..8055eb0b 100644
--- a/linux-build-farm/README.md
+++ b/linux-build-farm/README.md
@@ -4,3 +4,5 @@ Dockerized Linux Build Farm
This subfolder contains Dockerfiles and a script to build Linux packages for a variety of Linux distributions. It's also an excellent way to test your CPU fans and stress test your disk.
Running `build.sh` with no arguments builds everything. You can run `build.sh` with the name of a distro (e.g. centos-7) to only build that. Both 32 and 64 bit packages are built except where no 32-bit version of the distribution exists.
+
+The `make-apt-repos.sh` and `make-rpm-repos.sh` scripts build repositories. They may require some editing for outside-of-ZeroTier use, and be careful with the apt one if you have an existing *aptly* configuration.
diff --git a/linux-build-farm/debian-jessie/x64/Dockerfile b/linux-build-farm/debian-jessie/x64/Dockerfile
index 9591eff6..316c1d83 100644
--- a/linux-build-farm/debian-jessie/x64/Dockerfile
+++ b/linux-build-farm/debian-jessie/x64/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
+RUN dpkg --purge libhttp-parser-dev
+
ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/debian-jessie/x86/Dockerfile b/linux-build-farm/debian-jessie/x86/Dockerfile
index 9ed826ff..3ad83329 100644
--- a/linux-build-farm/debian-jessie/x86/Dockerfile
+++ b/linux-build-farm/debian-jessie/x86/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
+RUN dpkg --purge libhttp-parser-dev
+
ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/debian-stretch/x64/Dockerfile b/linux-build-farm/debian-stretch/x64/Dockerfile
new file mode 100644
index 00000000..c973c2b7
--- /dev/null
+++ b/linux-build-farm/debian-stretch/x64/Dockerfile
@@ -0,0 +1,12 @@
+FROM debian:stretch
+MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
+
+RUN apt-get update
+RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang
+
+#RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
+#RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
+
+RUN dpkg --purge libhttp-parser-dev
+
+ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/debian-stretch/x86/Dockerfile b/linux-build-farm/debian-stretch/x86/Dockerfile
new file mode 100644
index 00000000..bfc7a86f
--- /dev/null
+++ b/linux-build-farm/debian-stretch/x86/Dockerfile
@@ -0,0 +1,12 @@
+FROM mcandre/docker-debian-32bit:stretch
+MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
+
+RUN apt-get update
+RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang
+
+#RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
+#RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
+
+RUN dpkg --purge libhttp-parser-dev
+
+ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/debian-wheezy/x64/Dockerfile b/linux-build-farm/debian-wheezy/x64/Dockerfile
new file mode 100644
index 00000000..77e1c325
--- /dev/null
+++ b/linux-build-farm/debian-wheezy/x64/Dockerfile
@@ -0,0 +1,12 @@
+FROM debian:wheezy
+MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
+
+RUN apt-get update
+RUN apt-get install -y build-essential debhelper ruby-ronn g++ make devscripts
+
+RUN dpkg --purge libhttp-parser-dev
+
+ADD zt1-src.tar.gz /
+
+RUN mv -f /ZeroTierOne/debian/control.wheezy /ZeroTierOne/debian/control
+RUN mv -f /ZeroTierOne/debian/rules.wheezy /ZeroTierOne/debian/rules
diff --git a/linux-build-farm/debian-wheezy/x86/Dockerfile b/linux-build-farm/debian-wheezy/x86/Dockerfile
new file mode 100644
index 00000000..1f0117d2
--- /dev/null
+++ b/linux-build-farm/debian-wheezy/x86/Dockerfile
@@ -0,0 +1,15 @@
+#FROM tubia/debian:wheezy
+#MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
+
+#RUN apt-get update
+#RUN apt-get install -y build-essential debhelper ruby-ronn g++ make devscripts
+
+FROM zerotier/zt1-build-debian-wheezy-x86-base
+MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
+
+RUN dpkg --purge libhttp-parser-dev
+
+ADD zt1-src.tar.gz /
+
+RUN mv -f /ZeroTierOne/debian/control.wheezy /ZeroTierOne/debian/control
+RUN mv -f /ZeroTierOne/debian/rules.wheezy /ZeroTierOne/debian/rules
diff --git a/linux-build-farm/fedora-22/x64/Dockerfile b/linux-build-farm/fedora-22/x64/Dockerfile
index 554ca338..6da0a921 100644
--- a/linux-build-farm/fedora-22/x64/Dockerfile
+++ b/linux-build-farm/fedora-22/x64/Dockerfile
@@ -4,4 +4,7 @@ MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN yum update -y
RUN yum install -y make rpmdevtools gcc-c++ rubygem-ronn json-parser-devel lz4-devel http-parser-devel libnatpmp-devel
+RUN rpm --erase http-parser-devel
+RUN yum install -y rubygem-ronn ruby
+
ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/fedora-22/x86/Dockerfile b/linux-build-farm/fedora-22/x86/Dockerfile
index 504645c2..3c24b844 100644
--- a/linux-build-farm/fedora-22/x86/Dockerfile
+++ b/linux-build-farm/fedora-22/x86/Dockerfile
@@ -11,4 +11,9 @@
FROM zerotier/zt1-build-fedora-22-x86-base
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
+RUN echo 'i686-redhat-linux' >/etc/rpm/platform
+
+RUN rpm --erase http-parser-devel
+RUN yum install -y rubygem-ronn ruby
+
ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/make-apt-repos.sh b/linux-build-farm/make-apt-repos.sh
new file mode 100755
index 00000000..7a81cc5c
--- /dev/null
+++ b/linux-build-farm/make-apt-repos.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# This builds a series of Debian repositories for each distribution.
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
+
+for distro in debian-* ubuntu-*; do
+ if [ -n "`find ${distro} -name '*.deb' -type f`" ]; then
+ arches=`ls ${distro}/*.deb | cut -d _ -f 3 | cut -d . -f 1 | xargs | sed 's/ /,/g'`
+ distro_name=`echo $distro | cut -d '-' -f 2`
+ echo '---' $distro / $distro_name / $arches
+ aptly repo create -architectures=${arches} -comment="ZeroTier, Inc. Debian Packages" -component="main" -distribution=${distro_name} zt-release-${distro_name}
+ aptly repo add zt-release-${distro_name} ${distro}/*.deb
+ aptly publish repo zt-release-${distro_name} $distro_name
+ fi
+done
diff --git a/linux-build-farm/make-rpm-repos.sh b/linux-build-farm/make-rpm-repos.sh
new file mode 100755
index 00000000..0ed1cfe4
--- /dev/null
+++ b/linux-build-farm/make-rpm-repos.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
+
+GPG_KEY=contact@zerotier.com
+
+rm -rf /tmp/zt-rpm-repo
+mkdir /tmp/zt-rpm-repo
+
+for distro in centos-* fedora-* amazon-*; do
+ dname=`echo $distro | cut -d '-' -f 1`
+ if [ "$dname" = "centos" ]; then
+ dname=el
+ fi
+ if [ "$dname" = "fedora" ]; then
+ dname=fc
+ fi
+ if [ "$dname" = "amazon" ]; then
+ dname=amzn1
+ fi
+ dvers=`echo $distro | cut -d '-' -f 2`
+
+ mkdir -p /tmp/zt-rpm-repo/$dname/$dvers
+
+ cp -v $distro/*.rpm /tmp/zt-rpm-repo/$dname/$dvers
+done
+
+rpmsign --resign --key-id=$GPG_KEY --digest-algo=sha256 `find /tmp/zt-rpm-repo -type f -name '*.rpm'`
+
+for db in `find /tmp/zt-rpm-repo -mindepth 2 -maxdepth 2 -type d`; do
+ createrepo --database $db
+done
+
+# Stupid RHEL stuff
+cd /tmp/zt-rpm-repo/el
+ln -sf 6 6Client
+ln -sf 6 6Workstation
+ln -sf 6 6Server
+ln -sf 6 6.0
+ln -sf 6 6.1
+ln -sf 6 6.2
+ln -sf 6 6.3
+ln -sf 6 6.4
+ln -sf 6 6.5
+ln -sf 6 6.6
+ln -sf 6 6.7
+ln -sf 6 6.8
+ln -sf 6 6.9
+ln -sf 7 7Client
+ln -sf 7 7Workstation
+ln -sf 7 7Server
+ln -sf 7 7.0
+ln -sf 7 7.1
+ln -sf 7 7.2
+ln -sf 7 7.3
+ln -sf 7 7.4
+ln -sf 7 7.5
+ln -sf 7 7.6
+ln -sf 7 7.7
+ln -sf 7 7.8
+ln -sf 7 7.9
+
+echo
+echo Repo created in /tmp/zt-rpm-repo
diff --git a/linux-build-farm/ubuntu-trusty/x64/Dockerfile b/linux-build-farm/ubuntu-trusty/x64/Dockerfile
index 6ec65d2f..f84cc6e3 100644
--- a/linux-build-farm/ubuntu-trusty/x64/Dockerfile
+++ b/linux-build-farm/ubuntu-trusty/x64/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
RUN ln -sf /usr/bin/clang++-3.6 /usr/bin/clang++
RUN ln -sf /usr/bin/clang-3.6 /usr/bin/clang
+RUN dpkg --purge libhttp-parser-dev
+
ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-trusty/x86/Dockerfile b/linux-build-farm/ubuntu-trusty/x86/Dockerfile
index 271c19bb..6be3ae87 100644
--- a/linux-build-farm/ubuntu-trusty/x86/Dockerfile
+++ b/linux-build-farm/ubuntu-trusty/x86/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
RUN ln -sf /usr/bin/clang++-3.6 /usr/bin/clang++
RUN ln -sf /usr/bin/clang-3.6 /usr/bin/clang
+RUN dpkg --purge libhttp-parser-dev
+
ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-wily/x64/Dockerfile b/linux-build-farm/ubuntu-wily/x64/Dockerfile
index f56344cf..99b8d34c 100644
--- a/linux-build-farm/ubuntu-wily/x64/Dockerfile
+++ b/linux-build-farm/ubuntu-wily/x64/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
RUN ln -sf /usr/bin/clang++-3.7 /usr/bin/clang++
RUN ln -sf /usr/bin/clang-3.7 /usr/bin/clang
+RUN dpkg --purge libhttp-parser-dev
+
ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-wily/x86/Dockerfile b/linux-build-farm/ubuntu-wily/x86/Dockerfile
index 24bb1116..86ad14f2 100644
--- a/linux-build-farm/ubuntu-wily/x86/Dockerfile
+++ b/linux-build-farm/ubuntu-wily/x86/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
RUN ln -sf /usr/bin/clang++-3.7 /usr/bin/clang++
RUN ln -sf /usr/bin/clang-3.7 /usr/bin/clang
+RUN dpkg --purge libhttp-parser-dev
+
ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-xenial/x64/Dockerfile b/linux-build-farm/ubuntu-xenial/x64/Dockerfile
index e169843a..fa665a0a 100644
--- a/linux-build-farm/ubuntu-xenial/x64/Dockerfile
+++ b/linux-build-farm/ubuntu-xenial/x64/Dockerfile
@@ -4,7 +4,11 @@ MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN apt-get update
RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.8
-RUN ln -sf /usr/bin/clang++-3.8 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.8 /usr/bin/clang
+#RUN ln -sf /usr/bin/clang++-3.8 /usr/bin/clang++
+#RUN ln -sf /usr/bin/clang-3.8 /usr/bin/clang
+
+RUN rm -f /usr/bin/clang++ /usr/bin/clang
+
+RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-xenial/x86/Dockerfile b/linux-build-farm/ubuntu-xenial/x86/Dockerfile
index fdee712f..d01eec9b 100644
--- a/linux-build-farm/ubuntu-xenial/x86/Dockerfile
+++ b/linux-build-farm/ubuntu-xenial/x86/Dockerfile
@@ -4,7 +4,11 @@ MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN apt-get update
RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.8
-RUN ln -sf /usr/bin/clang++-3.8 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.8 /usr/bin/clang
+#RUN ln -sf /usr/bin/clang++-3.8 /usr/bin/clang++
+#RUN ln -sf /usr/bin/clang-3.8 /usr/bin/clang
+
+RUN rm -f /usr/bin/clang++ /usr/bin/clang
+
+RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /
diff --git a/make-linux.mk b/make-linux.mk
index 13b15476..acc22a63 100644
--- a/make-linux.mk
+++ b/make-linux.mk
@@ -98,8 +98,8 @@ endif
ifeq ($(ZT_DEBUG),1)
DEFS+=-DZT_TRACE
- override CFLAGS+=-Wall -g -pthread $(INCLUDES) $(DEFS)
- override CXXFLAGS+=-Wall -g -pthread $(INCLUDES) $(DEFS)
+ override CFLAGS+=-Wall -g -O -pthread $(INCLUDES) $(DEFS)
+ override CXXFLAGS+=-Wall -g -O -pthread $(INCLUDES) $(DEFS)
LDFLAGS=
STRIP?=echo
# The following line enables optimization for the crypto code, since
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index 871297f7..37af8425 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -43,8 +43,23 @@ namespace ZeroTier {
bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
{
const Address sourceAddress(source());
+
try {
- if ((cipher() == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
+ // Check for trusted paths or unencrypted HELLOs (HELLO is the only packet sent in the clear)
+ const unsigned int c = cipher();
+ bool trusted = false;
+ if (c == ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH) {
+ // If this is marked as a packet via a trusted path, check source address and path ID.
+ // Obviously if no trusted paths are configured this always returns false and such
+ // packets are dropped on the floor.
+ if (RR->topology->shouldInboundPathBeTrusted(_remoteAddress,trustedPathId())) {
+ trusted = true;
+ TRACE("TRUSTED PATH packet approved from %s(%s), trusted path ID %llx",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId());
+ } else {
+ TRACE("dropped packet from %s(%s), cipher set to trusted path mode but path %llx@%s is not trusted!",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId(),_remoteAddress.toString().c_str());
+ return true;
+ }
+ } else if ((c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
// Unencrypted HELLOs require some potentially expensive verification, so
// do this in the background if background processing is enabled.
if ((RR->dpEnabled > 0)&&(!deferred)) {
@@ -61,12 +76,15 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
if (peer) {
- if (!dearmor(peer->key())) {
- TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),size());
- return true;
+ if (!trusted) {
+ if (!dearmor(peer->key())) {
+ TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),size());
+ return true;
+ }
}
+
if (!uncompress()) {
- TRACE("dropped packet from %s(%s), compressed data invalid",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
+ TRACE("dropped packet from %s(%s), compressed data invalid",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str());
return true;
}
diff --git a/node/Node.cpp b/node/Node.cpp
index bedbba94..13085028 100644
--- a/node/Node.cpp
+++ b/node/Node.cpp
@@ -447,6 +447,7 @@ ZT_PeerList *Node::peers() const
p->paths[p->pathCount].lastReceive = path->lastReceived();
p->paths[p->pathCount].active = path->active(_now) ? 1 : 0;
p->paths[p->pathCount].preferred = ((bestPath)&&(*path == *bestPath)) ? 1 : 0;
+ p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust(path->address());
++p->pathCount;
}
}
@@ -745,6 +746,11 @@ 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);
}
+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
/****************************************************************************/
@@ -1014,6 +1020,13 @@ void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs)
} catch ( ... ) {}
}
+void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
+{
+ try {
+ reinterpret_cast<ZeroTier::Node *>(node)->setTrustedPaths(networks,ids,count);
+ } catch ( ... ) {}
+}
+
void ZT_Node_backgroundThreadMain(ZT_Node *node)
{
try {
diff --git a/node/Node.hpp b/node/Node.hpp
index 6ac23ca0..0a39d1ee 100644
--- a/node/Node.hpp
+++ b/node/Node.hpp
@@ -248,26 +248,15 @@ 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); }
- /**
- * @return True if we appear to be online
- */
inline bool online() const throw() { return _online; }
#ifdef ZT_TRACE
void postTrace(const char *module,unsigned int line,const char *fmt,...);
#endif
- /**
- * @return Next 64-bit random number (not for cryptographic use)
- */
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 setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
private:
inline SharedPtr<Network> _network(uint64_t nwid) const
diff --git a/node/Packet.hpp b/node/Packet.hpp
index 79fff344..3d95b0ba 100644
--- a/node/Packet.hpp
+++ b/node/Packet.hpp
@@ -57,11 +57,13 @@
* + Supports in-band world (root server definition) updates
* + Clustering! (Though this will work with protocol v4 clients.)
* + Otherwise backward compatible with protocol v4
- * 6 - 1.1.5 ... CURRENT
+ * 6 - 1.1.5 ... 1.1.10
* + Deprecate old dictionary-based network config format
* + Introduce new binary serialized network config and meta-data
+ * 7 - 1.1.10 -- CURRENT
+ * + Introduce trusted paths for local SDN use
*/
-#define ZT_PROTO_VERSION 6
+#define ZT_PROTO_VERSION 7
/**
* Minimum supported protocol version
@@ -100,10 +102,21 @@
#define ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 1
/**
- * DEPRECATED payload encrypted flag, will be removed for re-use soon.
+ * Cipher suite: NONE
*
- * This has been replaced by the three-bit cipher suite selection field where
- * a value of 0 indicates unencrypted (but authenticated) messages.
+ * This differs from POLY1305/NONE in that *no* crypto is done, not even
+ * authentication. This is for trusted local LAN interconnects for internal
+ * 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
@@ -337,7 +350,7 @@ namespace ZeroTier {
* <[5] destination ZT address>
* <[5] source ZT address>
* <[1] flags/cipher/hops>
- * <[8] 64-bit MAC>
+ * <[8] 64-bit MAC (or trusted path ID in trusted path mode)>
* [... -- begin encryption envelope -- ...]
* <[1] encrypted flags (MS 3 bits) and verb (LS 5 bits)>
* [... verb-specific payload ...]
@@ -1218,7 +1231,6 @@ public:
*/
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);
}
@@ -1229,13 +1241,31 @@ public:
{
unsigned char &b = (*this)[ZT_PACKET_IDX_FLAGS];
b = (b & 0xc7) | (unsigned char)((c << 3) & 0x38); // bits: FFCCCHHH
- // DEPRECATED "encrypted" flag -- used by pre-1.0.3 peers
+ // Set DEPRECATED "encrypted" flag -- used by pre-1.0.3 peers
if (c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
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)
*
* @return Packet ID
@@ -1278,6 +1308,10 @@ public:
/**
* 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
* @return False if packet is invalid or failed MAC authenticity check
*/
diff --git a/node/README.md b/node/README.md
new file mode 100644
index 00000000..01378c75
--- /dev/null
+++ b/node/README.md
@@ -0,0 +1,14 @@
+ZeroTier Virtual Switch Core
+======
+
+This directory contains the *real* ZeroTier: a completely OS-independent global virtual Ethernet switch engine. This is where the magic happens.
+
+Give it wire packets and it gives you Ethernet packets, and vice versa. The core contains absolutely no actual I/O, port configuration, or other OS-specific code (except Utils::getSecureRandom()). It provides a simple C API via [/include/ZeroTierOne.h](../include/ZeroTierOne.h). It's designed to be small and maximally portable for future use on small embedded and special purpose systems.
+
+Code in here follows these guidelines:
+
+ - Keep it minimal, especially in terms of code footprint and memory use.
+ - There should be no OS-dependent code here unless absolutely necessary (e.g. getSecureRandom).
+ - If it's not part of the core virtual Ethernet switch it does not belong here.
+ - No C++11 or C++14 since older and embedded compilers don't support it yet and this should be maximally portable.
+ - Minimize the use of complex C++ features since at some point we might end up "minus-minus'ing" this code if doing so proves necessary to port to tiny embedded systems.
diff --git a/node/Switch.cpp b/node/Switch.cpp
index b134cc69..bf3afe33 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -849,7 +849,12 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
unsigned int chunkSize = std::min(tmp.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU);
tmp.setFragmented(chunkSize < tmp.size());
- tmp.armor(peer->key(),encrypt);
+ const uint64_t trustedPathId = RR->topology->getOutboundPathTrust(viaPath->address());
+ if (trustedPathId) {
+ tmp.setTrusted(trustedPathId);
+ } else {
+ tmp.armor(peer->key(),encrypt);
+ }
if (viaPath->send(RR,tmp.data(),chunkSize,now)) {
if (chunkSize < tmp.size()) {
diff --git a/node/Topology.hpp b/node/Topology.hpp
index 86fbb011..03c491e5 100644
--- a/node/Topology.hpp
+++ b/node/Topology.hpp
@@ -28,6 +28,7 @@
#include <utility>
#include "Constants.hpp"
+#include "../include/ZeroTierOne.h"
#include "Address.hpp"
#include "Identity.hpp"
@@ -252,12 +253,64 @@ public:
*/
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:
Identity _getIdentity(const Address &zta);
void _setWorld(const World &newWorld);
const RuntimeEnvironment *const RR;
+ uint64_t _trustedPathIds[ZT_MAX_TRUSTED_PATHS];
+ InetAddress _trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
+ unsigned int _trustedPathCount;
World _world;
Hashtable< Address,SharedPtr<Peer> > _peers;
std::vector< Address > _rootAddresses;
diff --git a/one.cpp b/one.cpp
index 6bd5a34d..9f7a0a29 100644
--- a/one.cpp
+++ b/one.cpp
@@ -97,21 +97,21 @@ static void cliPrintHelp(const char *pn,FILE *out)
fprintf(out,
COPYRIGHT_NOTICE ZT_EOL_S
LICENSE_GRANT ZT_EOL_S);
- fprintf(out,"Usage: %s [-switches] <command/path> [<args>]"ZT_EOL_S""ZT_EOL_S,pn);
- fprintf(out,"Available switches:"ZT_EOL_S);
- fprintf(out," -h - Display this help"ZT_EOL_S);
- fprintf(out," -v - Show version"ZT_EOL_S);
- fprintf(out," -j - Display full raw JSON output"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," -T<token> - Authentication token (default: auto)"ZT_EOL_S);
- fprintf(out,ZT_EOL_S"Available commands:"ZT_EOL_S);
- fprintf(out," info - Display status info"ZT_EOL_S);
- fprintf(out," listpeers - List all peers"ZT_EOL_S);
- fprintf(out," listnetworks - List all networks"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," set <network> <setting> - Set a network setting"ZT_EOL_S);
+ fprintf(out,"Usage: %s [-switches] <command/path> [<args>]" ZT_EOL_S"" ZT_EOL_S,pn);
+ fprintf(out,"Available switches:" ZT_EOL_S);
+ fprintf(out," -h - Display this help" ZT_EOL_S);
+ fprintf(out," -v - Show version" ZT_EOL_S);
+ fprintf(out," -j - Display full raw JSON output" 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," -T<token> - Authentication token (default: auto)" ZT_EOL_S);
+ fprintf(out,ZT_EOL_S"Available commands:" ZT_EOL_S);
+ fprintf(out," info - Display status info" ZT_EOL_S);
+ fprintf(out," listpeers - List all peers" ZT_EOL_S);
+ fprintf(out," listnetworks - List all networks" 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," set <network> <setting> - Set a network setting" ZT_EOL_S);
}
static std::string cliFixJsonCRs(const std::string &s)
@@ -194,7 +194,7 @@ static int cli(int argc,char **argv)
cliPrintHelp(argv[0],stdout);
return 1;
}
- printf("%d.%d.%d"ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
+ printf("%d.%d.%d" ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
return 0;
case 'h':
@@ -216,7 +216,7 @@ static int cli(int argc,char **argv)
if ((!port)||(!authToken.length())) {
if (!homeDir.length()) {
- fprintf(stderr,"%s: missing port or authentication token and no home directory specified to auto-detect"ZT_EOL_S,argv[0]);
+ fprintf(stderr,"%s: missing port or authentication token and no home directory specified to auto-detect" ZT_EOL_S,argv[0]);
return 2;
}
@@ -225,7 +225,7 @@ static int cli(int argc,char **argv)
OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),portStr);
port = Utils::strToUInt(portStr.c_str());
if ((port == 0)||(port > 0xffff)) {
- fprintf(stderr,"%s: missing port and zerotier-one.port not found in %s"ZT_EOL_S,argv[0],homeDir.c_str());
+ fprintf(stderr,"%s: missing port and zerotier-one.port not found in %s" ZT_EOL_S,argv[0],homeDir.c_str());
return 2;
}
}
@@ -247,7 +247,7 @@ static int cli(int argc,char **argv)
}
#endif
if (!authToken.length()) {
- fprintf(stderr,"%s: missing authentication token and authtoken.secret not found (or readable) in %s"ZT_EOL_S,argv[0],homeDir.c_str());
+ fprintf(stderr,"%s: missing authentication token and authtoken.secret not found (or readable) in %s" ZT_EOL_S,argv[0],homeDir.c_str());
return 2;
}
}
@@ -279,7 +279,7 @@ static int cli(int argc,char **argv)
printf("%s",cliFixJsonCRs(responseBody).c_str());
return 0;
} 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;
}
} else if ((command == "info")||(command == "status")) {
@@ -312,7 +312,7 @@ static int cli(int argc,char **argv)
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);
+ printf("200 info %s %s %s" ZT_EOL_S,address,(online ? "ONLINE" : "OFFLINE"),version);
good = true;
}
}
@@ -321,12 +321,12 @@ static int cli(int argc,char **argv)
if (good) {
return 0;
} else {
- printf("%u %s invalid JSON response"ZT_EOL_S,scode,command.c_str());
+ printf("%u %s invalid JSON response" ZT_EOL_S,scode,command.c_str());
return 1;
}
}
} 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;
}
} else if (command == "listpeers") {
@@ -343,7 +343,7 @@ static int cli(int argc,char **argv)
printf("%s",cliFixJsonCRs(responseBody).c_str());
return 0;
} else {
- printf("200 listpeers <ztaddr> <paths> <latency> <version> <role>"ZT_EOL_S);
+ 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) {
@@ -417,7 +417,7 @@ static int cli(int argc,char **argv)
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);
+ printf("200 listpeers %s %s %lld %s %s" ZT_EOL_S,address,(paths.length()) ? paths.c_str() : "-",(long long)latency,verstr,role);
}
}
}
@@ -427,7 +427,7 @@ static int cli(int argc,char **argv)
return 0;
}
} 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;
}
} else if (command == "listnetworks") {
@@ -444,7 +444,7 @@ static int cli(int argc,char **argv)
printf("%s",cliFixJsonCRs(responseBody).c_str());
return 0;
} else {
- printf("200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>"ZT_EOL_S);
+ printf("200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>" ZT_EOL_S);
json_value *j = json_parse(responseBody.c_str(),responseBody.length());
if (j) {
if (j->type == json_array) {
@@ -483,7 +483,7 @@ static int cli(int argc,char **argv)
}
}
if ((nwid)&&(mac)&&(status)&&(type)) {
- printf("200 listnetworks %s %s %s %s %s %s %s"ZT_EOL_S,
+ printf("200 listnetworks %s %s %s %s %s %s %s" ZT_EOL_S,
nwid,
(((name)&&(name[0])) ? name : "-"),
mac,
@@ -499,7 +499,7 @@ static int cli(int argc,char **argv)
}
}
} 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;
}
} else if (command == "join") {
@@ -523,11 +523,11 @@ static int cli(int argc,char **argv)
if (json) {
printf("%s",cliFixJsonCRs(responseBody).c_str());
} else {
- printf("200 join OK"ZT_EOL_S);
+ printf("200 join OK" ZT_EOL_S);
}
return 0;
} 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;
}
} else if (command == "leave") {
@@ -547,11 +547,11 @@ static int cli(int argc,char **argv)
if (json) {
printf("%s",cliFixJsonCRs(responseBody).c_str());
} else {
- printf("200 leave OK"ZT_EOL_S);
+ printf("200 leave OK" ZT_EOL_S);
}
return 0;
} 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;
}
} else if (command == "set") {
@@ -584,7 +584,7 @@ static int cli(int argc,char **argv)
printf("%s",cliFixJsonCRs(responseBody).c_str());
return 0;
} 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;
}
}
@@ -613,13 +613,13 @@ static void idtoolPrintHelp(FILE *out,const char *pn)
fprintf(out,
COPYRIGHT_NOTICE ZT_EOL_S
LICENSE_GRANT ZT_EOL_S);
- fprintf(out,"Usage: %s <command> [<args>]"ZT_EOL_S""ZT_EOL_S"Commands:"ZT_EOL_S,pn);
- fprintf(out," generate [<identity.secret>] [<identity.public>] [<vanity>]"ZT_EOL_S);
- fprintf(out," validate <identity.secret/public>"ZT_EOL_S);
- fprintf(out," getpublic <identity.secret>"ZT_EOL_S);
- fprintf(out," sign <identity.secret> <file>"ZT_EOL_S);
- fprintf(out," verify <identity.secret/public> <file> <signature>"ZT_EOL_S);
- fprintf(out," mkcom <identity.secret> [<id,value,maxDelta> ...] (hexadecimal integers)"ZT_EOL_S);
+ fprintf(out,"Usage: %s <command> [<args>]" ZT_EOL_S"" ZT_EOL_S"Commands:" ZT_EOL_S,pn);
+ fprintf(out," generate [<identity.secret>] [<identity.public>] [<vanity>]" ZT_EOL_S);
+ fprintf(out," validate <identity.secret/public>" ZT_EOL_S);
+ fprintf(out," getpublic <identity.secret>" ZT_EOL_S);
+ fprintf(out," sign <identity.secret> <file>" ZT_EOL_S);
+ fprintf(out," verify <identity.secret/public> <file> <signature>" ZT_EOL_S);
+ fprintf(out," mkcom <identity.secret> [<id,value,maxDelta> ...] (hexadecimal integers)" ZT_EOL_S);
}
static Identity getIdFromArg(char *arg)
@@ -675,15 +675,15 @@ static int idtool(int argc,char **argv)
std::string idser = id.toString(true);
if (argc >= 3) {
if (!OSUtils::writeFile(argv[2],idser)) {
- fprintf(stderr,"Error writing to %s"ZT_EOL_S,argv[2]);
+ fprintf(stderr,"Error writing to %s" ZT_EOL_S,argv[2]);
return 1;
- } else printf("%s written"ZT_EOL_S,argv[2]);
+ } else printf("%s written" ZT_EOL_S,argv[2]);
if (argc >= 4) {
idser = id.toString(false);
if (!OSUtils::writeFile(argv[3],idser)) {
- fprintf(stderr,"Error writing to %s"ZT_EOL_S,argv[3]);
+ fprintf(stderr,"Error writing to %s" ZT_EOL_S,argv[3]);
return 1;
- } else printf("%s written"ZT_EOL_S,argv[3]);
+ } else printf("%s written" ZT_EOL_S,argv[3]);
}
} else printf("%s",idser.c_str());
} else if (!strcmp(argv[1],"validate")) {
@@ -694,14 +694,14 @@ static int idtool(int argc,char **argv)
Identity id = getIdFromArg(argv[2]);
if (!id) {
- fprintf(stderr,"Identity argument invalid or file unreadable: %s"ZT_EOL_S,argv[2]);
+ fprintf(stderr,"Identity argument invalid or file unreadable: %s" ZT_EOL_S,argv[2]);
return 1;
}
if (!id.locallyValidate()) {
- fprintf(stderr,"%s FAILED validation."ZT_EOL_S,argv[2]);
+ fprintf(stderr,"%s FAILED validation." ZT_EOL_S,argv[2]);
return 1;
- } else printf("%s is a valid identity"ZT_EOL_S,argv[2]);
+ } else printf("%s is a valid identity" ZT_EOL_S,argv[2]);
} else if (!strcmp(argv[1],"getpublic")) {
if (argc < 3) {
idtoolPrintHelp(stdout,argv[0]);
@@ -710,7 +710,7 @@ static int idtool(int argc,char **argv)
Identity id = getIdFromArg(argv[2]);
if (!id) {
- fprintf(stderr,"Identity argument invalid or file unreadable: %s"ZT_EOL_S,argv[2]);
+ fprintf(stderr,"Identity argument invalid or file unreadable: %s" ZT_EOL_S,argv[2]);
return 1;
}
@@ -723,18 +723,18 @@ static int idtool(int argc,char **argv)
Identity id = getIdFromArg(argv[2]);
if (!id) {
- fprintf(stderr,"Identity argument invalid or file unreadable: %s"ZT_EOL_S,argv[2]);
+ fprintf(stderr,"Identity argument invalid or file unreadable: %s" ZT_EOL_S,argv[2]);
return 1;
}
if (!id.hasPrivate()) {
- fprintf(stderr,"%s does not contain a private key (must use private to sign)"ZT_EOL_S,argv[2]);
+ fprintf(stderr,"%s does not contain a private key (must use private to sign)" ZT_EOL_S,argv[2]);
return 1;
}
std::string inf;
if (!OSUtils::readFile(argv[3],inf)) {
- fprintf(stderr,"%s is not readable"ZT_EOL_S,argv[3]);
+ fprintf(stderr,"%s is not readable" ZT_EOL_S,argv[3]);
return 1;
}
C25519::Signature signature = id.sign(inf.data(),(unsigned int)inf.length());
@@ -747,21 +747,21 @@ static int idtool(int argc,char **argv)
Identity id = getIdFromArg(argv[2]);
if (!id) {
- fprintf(stderr,"Identity argument invalid or file unreadable: %s"ZT_EOL_S,argv[2]);
+ fprintf(stderr,"Identity argument invalid or file unreadable: %s" ZT_EOL_S,argv[2]);
return 1;
}
std::string inf;
if (!OSUtils::readFile(argv[3],inf)) {
- fprintf(stderr,"%s is not readable"ZT_EOL_S,argv[3]);
+ fprintf(stderr,"%s is not readable" ZT_EOL_S,argv[3]);
return 1;
}
std::string signature(Utils::unhex(argv[4]));
if ((signature.length() > ZT_ADDRESS_LENGTH)&&(id.verify(inf.data(),(unsigned int)inf.length(),signature.data(),(unsigned int)signature.length()))) {
- printf("%s signature valid"ZT_EOL_S,argv[3]);
+ printf("%s signature valid" ZT_EOL_S,argv[3]);
} else {
- fprintf(stderr,"%s signature check FAILED"ZT_EOL_S,argv[3]);
+ fprintf(stderr,"%s signature check FAILED" ZT_EOL_S,argv[3]);
return 1;
}
} else if (!strcmp(argv[1],"mkcom")) {
@@ -772,7 +772,7 @@ static int idtool(int argc,char **argv)
Identity id = getIdFromArg(argv[2]);
if ((!id)||(!id.hasPrivate())) {
- fprintf(stderr,"Identity argument invalid, does not include private key, or file unreadable: %s"ZT_EOL_S,argv[2]);
+ fprintf(stderr,"Identity argument invalid, does not include private key, or file unreadable: %s" ZT_EOL_S,argv[2]);
return 1;
}
@@ -787,7 +787,7 @@ static int idtool(int argc,char **argv)
}
}
if (!com.sign(id)) {
- fprintf(stderr,"Signature of certificate of membership failed."ZT_EOL_S);
+ fprintf(stderr,"Signature of certificate of membership failed." ZT_EOL_S);
return 1;
}
@@ -981,27 +981,27 @@ static void printHelp(const char *cn,FILE *out)
LICENSE_GRANT ZT_EOL_S);
std::string updateUrl(OneService::autoUpdateUrl());
if (updateUrl.length())
- fprintf(out,"Automatic updates enabled:"ZT_EOL_S" %s"ZT_EOL_S" (all updates are securely authenticated by 256-bit ECDSA signature)"ZT_EOL_S""ZT_EOL_S,updateUrl.c_str());
- fprintf(out,"Usage: %s [-switches] [home directory]"ZT_EOL_S""ZT_EOL_S,cn);
- fprintf(out,"Available switches:"ZT_EOL_S);
- fprintf(out," -h - Display this help"ZT_EOL_S);
- fprintf(out," -v - Show version"ZT_EOL_S);
- fprintf(out," -U - Run as unprivileged user (skip privilege check)"ZT_EOL_S);
- fprintf(out," -p<port> - Port for UDP and TCP/HTTP (default: 9993, 0 for random)"ZT_EOL_S);
+ fprintf(out,"Automatic updates enabled:" ZT_EOL_S" %s" ZT_EOL_S" (all updates are securely authenticated by 256-bit ECDSA signature)" ZT_EOL_S"" ZT_EOL_S,updateUrl.c_str());
+ fprintf(out,"Usage: %s [-switches] [home directory]" ZT_EOL_S"" ZT_EOL_S,cn);
+ fprintf(out,"Available switches:" ZT_EOL_S);
+ fprintf(out," -h - Display this help" ZT_EOL_S);
+ fprintf(out," -v - Show version" ZT_EOL_S);
+ fprintf(out," -U - Run as unprivileged user (skip privilege check)" ZT_EOL_S);
+ fprintf(out," -p<port> - Port for UDP and TCP/HTTP (default: 9993, 0 for random)" ZT_EOL_S);
#ifdef __UNIX_LIKE__
- fprintf(out," -d - Fork and run as daemon (Unix-ish OSes)"ZT_EOL_S);
+ fprintf(out," -d - Fork and run as daemon (Unix-ish OSes)" ZT_EOL_S);
#endif // __UNIX_LIKE__
#ifdef __WINDOWS__
- fprintf(out," -C - Run from command line instead of as service (Windows)"ZT_EOL_S);
- fprintf(out," -I - Install Windows service (Windows)"ZT_EOL_S);
- fprintf(out," -R - Uninstall Windows service (Windows)"ZT_EOL_S);
- fprintf(out," -D - Remove all instances of Windows tap device (Windows)"ZT_EOL_S);
+ fprintf(out," -C - Run from command line instead of as service (Windows)" ZT_EOL_S);
+ fprintf(out," -I - Install Windows service (Windows)" ZT_EOL_S);
+ fprintf(out," -R - Uninstall Windows service (Windows)" ZT_EOL_S);
+ fprintf(out," -D - Remove all instances of Windows tap device (Windows)" ZT_EOL_S);
#endif // __WINDOWS__
- fprintf(out," -i - Generate and manage identities (zerotier-idtool)"ZT_EOL_S);
- fprintf(out," -q - Query API (zerotier-cli)"ZT_EOL_S);
+ fprintf(out," -i - Generate and manage identities (zerotier-idtool)" ZT_EOL_S);
+ fprintf(out," -q - Query API (zerotier-cli)" ZT_EOL_S);
}
#ifdef __WINDOWS__
@@ -1079,7 +1079,7 @@ int main(int argc,char **argv)
break;
case 'v': // Display version
- printf("%d.%d.%d"ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
+ printf("%d.%d.%d" ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
return 0;
case 'i': // Invoke idtool personality
@@ -1101,12 +1101,12 @@ int main(int argc,char **argv)
case 'I': { // Install this binary as a Windows service
if (IsCurrentUserLocalAdministrator() != TRUE) {
- fprintf(stderr,"%s: must be run as a local administrator."ZT_EOL_S,argv[0]);
+ fprintf(stderr,"%s: must be run as a local administrator." ZT_EOL_S,argv[0]);
return 1;
}
std::string ret(InstallService(ZT_SERVICE_NAME,ZT_SERVICE_DISPLAY_NAME,ZT_SERVICE_START_TYPE,ZT_SERVICE_DEPENDENCIES,ZT_SERVICE_ACCOUNT,ZT_SERVICE_PASSWORD));
if (ret.length()) {
- fprintf(stderr,"%s: unable to install service: %s"ZT_EOL_S,argv[0],ret.c_str());
+ fprintf(stderr,"%s: unable to install service: %s" ZT_EOL_S,argv[0],ret.c_str());
return 3;
}
return 0;
@@ -1114,12 +1114,12 @@ int main(int argc,char **argv)
case 'R': { // Uninstall this binary as Windows service
if (IsCurrentUserLocalAdministrator() != TRUE) {
- fprintf(stderr,"%s: must be run as a local administrator."ZT_EOL_S,argv[0]);
+ fprintf(stderr,"%s: must be run as a local administrator." ZT_EOL_S,argv[0]);
return 1;
}
std::string ret(UninstallService(ZT_SERVICE_NAME));
if (ret.length()) {
- fprintf(stderr,"%s: unable to uninstall service: %s"ZT_EOL_S,argv[0],ret.c_str());
+ fprintf(stderr,"%s: unable to uninstall service: %s" ZT_EOL_S,argv[0],ret.c_str());
return 3;
}
return 0;
@@ -1128,7 +1128,7 @@ int main(int argc,char **argv)
case 'D': {
std::string err = WindowsEthernetTap::destroyAllPersistentTapDevices();
if (err.length() > 0) {
- fprintf(stderr,"%s: unable to uninstall one or more persistent tap devices: %s"ZT_EOL_S,argv[0],err.c_str());
+ fprintf(stderr,"%s: unable to uninstall one or more persistent tap devices: %s" ZT_EOL_S,argv[0],err.c_str());
return 3;
}
return 0;
@@ -1154,7 +1154,7 @@ int main(int argc,char **argv)
if (!homeDir.length())
homeDir = OneService::platformDefaultHomePath();
if (!homeDir.length()) {
- fprintf(stderr,"%s: no home path specified and no platform default available"ZT_EOL_S,argv[0]);
+ fprintf(stderr,"%s: no home path specified and no platform default available" ZT_EOL_S,argv[0]);
return 1;
} else {
std::vector<std::string> hpsp(Utils::split(homeDir.c_str(),ZT_PATH_SEPARATOR_S,"",""));
@@ -1175,14 +1175,14 @@ int main(int argc,char **argv)
#ifdef __UNIX_LIKE__
#ifndef ZT_ONE_NO_ROOT_CHECK
if ((!skipRootCheck)&&(getuid() != 0)) {
- fprintf(stderr,"%s: must be run as root (uid 0)"ZT_EOL_S,argv[0]);
+ fprintf(stderr,"%s: must be run as root (uid 0)" ZT_EOL_S,argv[0]);
return 1;
}
#endif // !ZT_ONE_NO_ROOT_CHECK
if (runAsDaemon) {
long p = (long)fork();
if (p < 0) {
- fprintf(stderr,"%s: could not fork"ZT_EOL_S,argv[0]);
+ fprintf(stderr,"%s: could not fork" ZT_EOL_S,argv[0]);
return 1;
} else if (p > 0)
return 0; // forked
@@ -1199,7 +1199,7 @@ int main(int argc,char **argv)
// Running in "interactive" mode (mostly for debugging)
if (IsCurrentUserLocalAdministrator() != TRUE) {
if (!skipRootCheck) {
- fprintf(stderr,"%s: must be run as a local administrator."ZT_EOL_S,argv[0]);
+ fprintf(stderr,"%s: must be run as a local administrator." ZT_EOL_S,argv[0]);
return 1;
}
} else {
@@ -1214,7 +1214,7 @@ int main(int argc,char **argv)
if (CServiceBase::Run(zt1Service) == TRUE) {
return 0;
} else {
- fprintf(stderr,"%s: unable to start service (try -h for help)"ZT_EOL_S,argv[0]);
+ fprintf(stderr,"%s: unable to start service (try -h for help)" ZT_EOL_S,argv[0]);
return 1;
}
}
@@ -1241,7 +1241,7 @@ int main(int argc,char **argv)
case OneService::ONE_NORMAL_TERMINATION:
break;
case OneService::ONE_UNRECOVERABLE_ERROR:
- fprintf(stderr,"%s: fatal error: %s"ZT_EOL_S,argv[0],zt1Service->fatalErrorMessage().c_str());
+ fprintf(stderr,"%s: fatal error: %s" ZT_EOL_S,argv[0],zt1Service->fatalErrorMessage().c_str());
returnValue = 1;
break;
case OneService::ONE_IDENTITY_COLLISION: {
diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp
index b10c4cd1..e336bb67 100644
--- a/osdep/LinuxEthernetTap.cpp
+++ b/osdep/LinuxEthernetTap.cpp
@@ -254,7 +254,7 @@ bool LinuxEthernetTap::removeIp(const InetAddress &ip)
if (!ip)
return true;
std::vector<InetAddress> allIps(ips());
- if (!std::binary_search(allIps.begin(),allIps.end(),ip)) {
+ if (std::find(allIps.begin(),allIps.end(),ip) != allIps.end()) {
if (___removeIp(_dev,ip))
return true;
}
@@ -294,7 +294,7 @@ std::vector<InetAddress> LinuxEthernetTap::ips() const
freeifaddrs(ifa);
std::sort(r.begin(),r.end());
- std::unique(r.begin(),r.end());
+ r.erase(std::unique(r.begin(),r.end()),r.end());
return r;
}
@@ -356,7 +356,7 @@ void LinuxEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,st
newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
std::sort(newGroups.begin(),newGroups.end());
- std::unique(newGroups.begin(),newGroups.end());
+ newGroups.erase(std::unique(newGroups.begin(),newGroups.end()),newGroups.end());
for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) {
if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m))
diff --git a/osdep/ManagedRoute.cpp b/osdep/ManagedRoute.cpp
index b87506c0..264866fc 100644
--- a/osdep/ManagedRoute.cpp
+++ b/osdep/ManagedRoute.cpp
@@ -56,6 +56,8 @@
#define ZT_LINUX_IP_COMMAND "/sbin/ip"
#define ZT_LINUX_IP_COMMAND_2 "/usr/sbin/ip"
+// NOTE: BSD is mostly tested on Apple/Mac but is likely to work on other BSD too
+
namespace ZeroTier {
namespace {
@@ -412,7 +414,7 @@ bool ManagedRoute::sync()
// Shadow system route if it exists, also delete any obsolete shadows
// and replace them with the new state. sync() is called periodically to
// allow us to do that if underlying connectivity changes.
- if ((_systemVia != newSystemVia)||(!strcmp(_systemDevice,newSystemDevice))) {
+ if ( ((_systemVia != newSystemVia)||(strcmp(_systemDevice,newSystemDevice))) && (strcmp(_device,newSystemDevice)) ) {
if ((_systemVia)&&(_systemDevice[0])) {
_routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
diff --git a/osdep/ManagedRoute.hpp b/osdep/ManagedRoute.hpp
index 5dde12ad..63310f24 100644
--- a/osdep/ManagedRoute.hpp
+++ b/osdep/ManagedRoute.hpp
@@ -54,8 +54,8 @@ public:
* "device name."
*
* @param target Route target (e.g. 0.0.0.0/0 for default)
- * @param via Route next L3 hop or NULL InetAddress if local
- * @param device Device name/ID if 'via' is null and route is local, otherwise ignored
+ * @param via Route next L3 hop or NULL InetAddress if local in which case it will be routed via device
+ * @param device Name or hex LUID of ZeroTier device (e.g. zt#)
* @return True if route was successfully set
*/
inline bool set(const InetAddress &target,const InetAddress &via,const char *device)
diff --git a/osdep/OSXEthernetTap.cpp b/osdep/OSXEthernetTap.cpp
index 89cbbdac..e5361ce4 100644
--- a/osdep/OSXEthernetTap.cpp
+++ b/osdep/OSXEthernetTap.cpp
@@ -473,7 +473,7 @@ bool OSXEthernetTap::addIp(const InetAddress &ip)
long cpid = (long)vfork();
if (cpid == 0) {
- ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
+ ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toString().c_str(),"alias",(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
@@ -493,7 +493,7 @@ bool OSXEthernetTap::removeIp(const InetAddress &ip)
if (*i == ip) {
long cpid = (long)vfork();
if (cpid == 0) {
- execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
+ execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toIpString().c_str(),"-alias",(const char *)0);
_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
diff --git a/selftest.cpp b/selftest.cpp
index 401d2595..f4232851 100644
--- a/selftest.cpp
+++ b/selftest.cpp
@@ -975,42 +975,6 @@ static int testPhy()
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()
{
std::cout << "[resolver] Testing BackgroundResolver..."; std::cout.flush();
@@ -1129,7 +1093,6 @@ int main(int argc,char **argv)
srand((unsigned int)time(0));
///*
- r |= testSqliteNetworkController();
r |= testOther();
r |= testCrypto();
r |= testPacket();
diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp
index 0e2b530d..a10697a9 100644
--- a/service/ControlPlane.cpp
+++ b/service/ControlPlane.cpp
@@ -190,13 +190,15 @@ static std::string _jsonEnumerate(unsigned int depth,const ZT_PeerPhysicalPath *
"%s\t\"lastSend\": %llu,\n"
"%s\t\"lastReceive\": %llu,\n"
"%s\t\"active\": %s,\n"
- "%s\t\"preferred\": %s\n"
+ "%s\t\"preferred\": %s,\n"
+ "%s\t\"trustedPathId\": %llu\n"
"%s}",
prefix,_jsonEscape(reinterpret_cast<const InetAddress *>(&(pp[i].address))->toString()).c_str(),
prefix,pp[i].lastSend,
prefix,pp[i].lastReceive,
prefix,(pp[i].active == 0) ? "false" : "true",
prefix,(pp[i].preferred == 0) ? "false" : "true",
+ prefix,pp[i].trustedPathId,
prefix);
buf.append(json);
}
diff --git a/service/OneService.cpp b/service/OneService.cpp
index c1b24050..13820f5c 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -759,6 +759,38 @@ public:
for(int i=0;i<3;++i)
_portsBE[i] = Utils::hton((uint16_t)_ports[i]);
+ {
+ FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S + "trustedpaths").c_str(),"r");
+ 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);
+ }
+ }
+
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
_controller = new SqliteNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str(),(_homePath + ZT_PATH_SEPARATOR_S + "circuitTestResults.d").c_str());
_node->setNetconfMaster((void *)_controller);
@@ -1041,13 +1073,13 @@ public:
// Begin private implementation methods
// Checks if a managed IP or route target is allowed
- bool checkIfManagedIsAllowed(const NetworkState &n,const InetAddress &addr)
+ bool checkIfManagedIsAllowed(const NetworkState &n,const InetAddress &target)
{
if (!n.settings.allowManaged)
return false;
- if (addr.isDefaultRoute())
+ if (target.isDefaultRoute())
return n.settings.allowDefault;
- switch(addr.ipScope()) {
+ switch(target.ipScope()) {
case InetAddress::IP_SCOPE_NONE:
case InetAddress::IP_SCOPE_MULTICAST:
case InetAddress::IP_SCOPE_LOOPBACK:
@@ -1060,6 +1092,16 @@ public:
}
}
+ // 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)
{
@@ -1075,18 +1117,18 @@ public:
std::sort(newManagedIps.begin(),newManagedIps.end());
newManagedIps.erase(std::unique(newManagedIps.begin(),newManagedIps.end()),newManagedIps.end());
- 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());
- }
- }
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);
}
@@ -1099,10 +1141,12 @@ public:
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<ManagedRoute>::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();) {
bool haveRoute = false;
- if (checkIfManagedIsAllowed(n,mr->target())) {
+ 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));
@@ -1124,7 +1168,7 @@ public:
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))
+ if ( (!checkIfManagedIsAllowed(n,*target)) || ((via->ss_family == target->ss_family)&&(matchIpOnly(myIps,*via))) )
continue;
bool haveRoute = false;
diff --git a/service/README.md b/service/README.md
index 05f340d8..75c437dd 100644
--- a/service/README.md
+++ b/service/README.md
@@ -120,133 +120,3 @@ Path objects describe direct physical paths to peer. If no path objects are list
<tr><td>fixed</td><td>boolean</td><td>If true, this is a statically-defined "fixed" path</td><td>no</td></tr>
<tr><td>preferred</td><td>boolean</td><td>If true, this is the current preferred path</td><td>no</td></tr>
</table>
-
-### Network Controller API
-
-If ZeroTier One was built with *ZT\_ENABLE\_NETWORK\_CONTROLLER* defined, the following API paths are available. Otherwise these paths will return 404.
-
-#### /controller
-
- * Purpose: Check for controller function and return controller status
- * Methods: GET
- * Returns: { object }
-
-<table>
-<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
-<tr><td>controller</td><td>boolean</td><td>Always 'true' if controller is running</td><td>no</td></tr>
-<tr><td>apiVersion</td><td>integer</td><td>JSON API version, currently 1</td><td>no</td></tr>
-<tr><td>clock</td><td>integer</td><td>Controller system clock in ms since epoch</td><td>no</td></tr>
-</table>
-
-#### /controller/network
-
- * Purpose: List all networks hosted by this controller
- * Methods: GET
- * Returns: [ string, ... ]
-
-This returns an array of 16-digit hexadecimal network IDs. Unlike /network under the top-level API, it does not dump full network information for all networks as this may be quite large for a large controller.
-
-#### /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.
-
-It's important to understand how network IDs work. The first ten digits (most significant 40 bits) of a network ID are the ZeroTier address of the controller. This is how clients find it. The last six digits (least significant 24 bits) are arbitrary and serve to identify the network uniquely on the controller.
-
-Thus a network's first ten digits *must* be the controller's address. If your controller is *deadbeef01*, then the networks it controls must have IDs like *deadbeef01feed02* or *deadbeef01beef03*. This API however *does not* enforce this requirement. It will allow you to add arbitrary network IDs, but they won't work since clients will never be able to find them. To create a new network with a random last six digits safely and atomically, you can POST to */controller/network/##########\_\_\_\_\_\_* where ########## is the controller's address and the underscores are as shown. This will pick a random unallocated network ID, which will be returned in the 'nwid' field of the returned JSON object.
-
-<table>
-<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
-<tr><td>nwid</td><td>string</td><td>16-digit hex network ID</td><td>no</td></tr>
-<tr><td>name</td><td>string</td><td>Short network name (max: 127 chars)</td><td>yes</td></tr>
-<tr><td>private</td><td>boolean</td><td>False if public network, true for access control</td><td>yes</td></tr>
-<tr><td>enableBroadcast</td><td>boolean</td><td>True to allow Ethernet broadcast (ff:ff:ff:ff:ff:ff)</td><td>yes</td></tr>
-<tr><td>allowPassiveBridging</td><td>boolean</td><td>True to allow any member to bridge (experimental!)</td><td>yes</td></tr>
-<tr><td>v4AssignMode</td><td>string</td><td>'none', 'zt', or 'dhcp' (see below)</td><td>yes</td></tr>
-<tr><td>v6AssignMode</td><td>string</td><td>'none', 'zt', or 'dhcp' (see below)</td><td>yes</td></tr>
-<tr><td>multicastLimit</td><td>integer</td><td>Maximum number of multicast recipients per multicast/broadcast address</td><td>yes</td></tr>
-<tr><td>creationTime</td><td>integer</td><td>Time network was created in ms since epoch</td><td>no</td></tr>
-<tr><td>revision</td><td>integer</td><td>Network config revision number</td><td>no</td></tr>
-<tr><td>memberRevisionCounter</td><td>integer</td><td>Current value of network revision counter (incremented after every member add or revision)</td><td>no</td></tr>
-<tr><td>clock</td><td>integer</td><td>Current clock in ms since epoch (for convenience)</td><td>no</td></tr>
-<tr><td>authorizedMemberCount</td><td>integer</td><td>Number of authorized members</td><td>no</td></tr>
-<tr><td>relays</td><td>[object]</td><td>Array of network-specific relay nodes (see below)</td><td>yes</td></tr>
-<tr><td>ipLocalRoutes</td><td>[string]</td><td>Array of IP network/netmask entries corresponding to networks routed directly via this interface (e.g. 10.0.0.0/8 to route 10.0.0.0 via this interface)</td></tr>
-<tr><td>ipAssignmentPools</td><td>[object]</td><td>Array of IP auto-assignment pools for 'zt' assignment mode</td><td>yes</td></tr>
-<tr><td>rules</td><td>[object]</td><td>Array of network flow rules (see below)</td><td>yes</td></tr>
-</table>
-
-The network member list includes both authorized and unauthorized members. DELETE unauthorized members to remove them from the list. Relays, IP assignment pools, and rules are edited via direct POSTs to the network object. New values replace all previous values.
-
-Networks must have rules. If there are no rules, the default action is 'deny'. As also documented in the Rule object definition below, rules currently only support etherType and allow/deny. Thus to make a functioning network, add etherType allow entries for IPV4/ARP and/or IPv6. Alternately you can add a null allow entry to allow all traffic, causing the network to behave like a normal pass-through switch.
-
-**Relay object format:**
-
-Relay objects define network-specific preferred relay nodes. Traffic to peers on this network will preferentially use these relays if they are available, and otherwise will fall back to the global rootserver infrastructure.
-
-<table>
-<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-<tr><td>address</td><td>string</td><td>10-digit ZeroTier address of relay node</td></tr>
-<tr><td>phyAddress</td><td>string</td><td>Fixed path address in IP/port format e.g. 192.168.1.1/9993</td></tr>
-</table>
-
-**IP assignment pool object format:**
-
-IP assignment pools are only used if they are within a network specified in ipLocalRoutes.
-
-<table>
-<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-<tr><td>ipRangeStart</td><td>string</td><td>Start of IP assignment range</td></tr>
-<tr><td>ipRangeEnd</td><td>string</td><td>End of IP assignment range</td></tr>
-</table>
-
-**Rule object format:**
-
- * **Note**: at the moment, <u>only rules specifying allowed Ethernet types are used</u>. The database supports a richer rule set, but this is not implemented yet in the client. <u>Other types of rules will have no effect</u> (yet).
-
-Rules are matched in order of ruleNo. If no rules match, the default action is 'drop'. To allow all traffic, create a single rule with all *null* fields and an action of 'accept'.
-
-Rule object fields can be *null*, in which case they are omitted from the object. A null field indicates "no match on this criteria."
-
-IP related fields apply only to Ethernet frames of type IPv4 or IPV6. Otherwise they are ignored.
-
-<table>
-<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-<tr><td>ruleNo</td><td>integer</td><td>User-defined rule ID and sort order</td></tr>
-<tr><td>nodeId</td><td>string</td><td>10-digit hex ZeroTier address of node if this rule is local to only one member</td></tr>
-<tr><td>sourcePort</td><td>string</td><td>10-digit hex ZeroTier address of source port on virtual switch (source device address)</td></tr>
-<tr><td>destPort</td><td>string</td><td>10-digit hex ZeroTier address of destination port on virtual switch (destination device address)</td></tr>
-<tr><td>vlanId</td><td>integer</td><td>Ethernet VLAN ID</td></tr>
-<tr><td>vlanPcp</td><td>integer</td><td>Ethernet VLAN priority code point (PCP) ID</td></tr>
-<tr><td>etherType</td><td>integer</td><td>Ethernet frame type</td></tr>
-<tr><td>macSource</td><td>string</td><td>Ethernet source MAC address</td></tr>
-<tr><td>macDest</td><td>string</td><td>Ethernet destination MAC address</td></tr>
-<tr><td>ipSource</td><td>string</td><td>Source IP address</td></tr>
-<tr><td>ipDest</td><td>string</td><td>Destination IP address</td></tr>
-<tr><td>ipTos</td><td>integer</td><td>IP TOS field</td></tr>
-<tr><td>ipProtocol</td><td>integer</td><td>IP protocol</td></tr>
-<tr><td>ipSourcePort</td><td>integer</td><td>IP source port</td></tr>
-<tr><td>ipDestPort</td><td>integer</td><td>IP destination port</td></tr>
-<tr><td>action</td><td>string</td><td>Rule action: accept, drop, etc.</td></tr>
-</table>
-
-#### /controller/network/\<network ID\>/member/\<address\>
-
- * Purpose: Create, authorize, or remove a network member
- * Methods: GET, POST, DELETE
- * Returns: { object }
-
-<table>
-<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
-<tr><td>nwid</td><td>string</td><td>16-digit hex network ID</td><td>no</td></tr>
-<tr><td>clock</td><td>integer</td><td>Current clock in ms since epoch (for convenience)</td><td>no</td></tr>
-<tr><td>address</td><td>string</td><td>10-digit hex ZeroTier address</td><td>no</td></tr>
-<tr><td>authorized</td><td>boolean</td><td>Is member authorized?</td><td>yes</td></tr>
-<tr><td>activeBridge</td><td>boolean</td><td>This member is an active network bridge</td><td>yes</td></tr>
-<tr><td>identity</td><td>string</td><td>Full ZeroTier identity of member</td><td>no</td></tr>
-<tr><td>ipAssignments</td><td>[string]</td><td>Array of IP/bits IP assignments</td><td>yes</td></tr>
-<tr><td>memberRevision</td><td>integer</td><td>Member revision counter value from network at time of last revision or member creation</td><td>no</td></tr>
-</table>
diff --git a/version.h b/version.h
index c925c43b..9ef01d6d 100644
--- a/version.h
+++ b/version.h
@@ -32,6 +32,6 @@
/**
* Revision
*/
-#define ZEROTIER_ONE_VERSION_REVISION 6
+#define ZEROTIER_ONE_VERSION_REVISION 12
#endif
diff --git a/zerotier-one.spec b/zerotier-one.spec
index f8ef0c3b..36856bd4 100644
--- a/zerotier-one.spec
+++ b/zerotier-one.spec
@@ -1,5 +1,5 @@
Name: zerotier-one
-Version: 1.1.6
+Version: 1.1.12
Release: 0.1%{?dist}
Summary: ZeroTier One network virtualization service
@@ -12,7 +12,6 @@ BuildRequires: systemd
%endif
%if 0%{?fedora} >= 21
-BuildRequires: http-parser-devel
BuildRequires: lz4-devel
BuildRequires: libnatpmp-devel
BuildRequires: systemd
@@ -30,19 +29,18 @@ Requires: chkconfig
%endif
%if 0%{?fedora} >= 21
-Requires: http-parser
Requires: lz4
Requires: libnatpmp
Requires: systemd
Requires: json-parser
%endif
+Provides: bundled(http-parser) = 2.7.0
Provides: bundled(miniupnpc) = 2.0
%if 0%{?rhel} >= 6
Provides: bundled(json-parser) = 1.1.0
Provides: bundled(lz4) = 1.7.1
-Provides: bundled(http-parser) = 2.7.0
Provides: bundled(libnatpmp) = 20131126
%endif
@@ -151,6 +149,12 @@ esac
%endif
%changelog
+* Tue Jul 12 2016 Adam Ierymenko <adam.ierymenko@zerotier.com> - 1.1.10-0.1
+- see https://github.com/zerotier/ZeroTierOne for release notes
+
+* Fri Jul 08 2016 Adam Ierymenko <adam.ierymenko@zerotier.com> - 1.1.8-0.1
+- see https://github.com/zerotier/ZeroTierOne for release notes
+
* Sat Jun 25 2016 Adam Ierymenko <adam.ierymenko@zerotier.com> - 1.1.6-0.1
- now builds on CentOS 6 as well as newer distros, and some cleanup