← Blog

ZeroTier on a microcontroller: an encrypted network leaf for Zephyr

ZeroTier creates a private, encrypted Ethernet network over the internet or a local network, and it normally runs on PCs, servers and routers. We got it running under Zephyr on a Teensy 4.1 (Cortex-M7, 600 MHz, 1 MB RAM). The board joins a private ZeroTier network alongside stock ZeroTier 1.16.2 peers and a stock controller, with encryption ending on the board itself rather than on a gateway. Ordinary Zephyr services like HTTP, Telnet and SNMP can then use the encrypted network without changes. The code is open source (MPL-2.0) at github.com/jerzydziewierz/zephyr-zerotier-leaf.

Why this is not a simple port

Although the ZeroTier One core is written in portable C++, it assumes PC-scale memory. Two allocations in the upstream source take more than 4 MiB by themselves:

On the board, the normal application RAM region is 256 KiB. Simply lowering the MTU or talking to fewer peers does not get rid of those two allocations.

The approach: a closed-fleet leaf

Instead of trying to join arbitrary ZeroTier networks, the board acts as a restricted leaf in a small network that you control:

The ZeroTier changes live in four reviewed patches against the pinned 1.16.2 source, verified at build time so the firmware cannot accidentally build against an unreviewed vendor tree. On the Zephyr side, the module provides a native Ethernet interface, a UDP transport thread, and start, status and stop functions.

Results

Four plots against offered rate of 1200-byte UDP echoes: loss stays near zero up to 200 per second, then rises; round-trip time stays near 7 ms up to 190 per second; echoed rate follows offered rate up to about 210 per second; board idle time falls from 95% to 3%.

One finding: signature checks and receive buffers

During the soak test, we saw about 2 lost echoes per minute, always at the same times. The module's diagnostic command (zt diag) pointed to the cause: every 60 seconds the core blocks to verify Ed25519 signatures, spending about 47 ms on the controller's configuration refresh and about 94 ms on a peer's membership certificate. While those checks run, the transport thread is not reading the network. Zephyr's 36 default receive buffers only hold about 3 encrypted datagrams, so the USB driver dropped whatever arrived next.

The fix was configuration rather than code: setting CONFIG_NET_BUF_RX_COUNT=96 at a cost of 10 KiB more RAM. With the larger buffer pool, 0 of 12,000 echoes were lost. We do not skip or delay the signature checks, since doing so would let the board accept membership or configuration that it had not verified.

On-board testing also uncovered a lock-up in the Zephyr USB network driver, which we covered in our previous post and sent upstream in Zephyr pull request #123515.

Limits

This is a prototype and is not ready for production. So far we have only tested it on one board type (Teensy 4.1, Zephyr v4.4.2) using USB as the underlying link; we have not yet run on-board tests with seven live peers, hostile traffic, membership revocation and expiry, or physical Ethernet. Persistent key storage and authenticated roster updates are also unimplemented, so today the firmware image contains the board's secret key. Finally, ZeroTier 1.x has no forward secrecy, and the transport does not stop replay of an authenticated frame. The repository README and VALIDATION.md list all limits alongside the test evidence.

Next

Next up, we plan to test seven live peers and hostile input directly on the board, and measure where in the i.MX RT1062 memory the crypto code runs fastest. This project is not affiliated with or endorsed by ZeroTier, Inc., though we thank the ZeroTier authors for a core clean enough to restrict this way.


Made with a notepad.