Open-source project
meh/rust-tun avatar
meh/rust-tun

rust-tun: creating TUN interfaces from Rust across nine platforms

TUN device creation and handling.

687 stars179 forksRustLicense varies

At a glance

What is it?
The tun crate creates and configures TUN network interfaces from Rust on Windows, Linux, macOS, FreeBSD, Android, iOS, tvOS and OpenHarmony, with a blocking API and an optional async mode for tokio and mio. It is a low-level building block for VPN and tunnel software, and it ships under the WTFPL.
Who is it for?
tun fits a Rust developer building a VPN client, a proxy that needs to own a virtual interface, or a network tool that must read raw IP packets, and who wants one crate rather than a per-platform binding. It does not fit an application that only needs to send UDP or TCP through a proxy, since this crate gives you a device and expects you to implement the protocol on top.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 29 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A device, not a tunnel: what the crate actually gives you

The description of the crate is short and worth expanding carefully, because the scope is narrower than the word tunnel suggests. It allows the creation and usage of TUN interfaces, and the aim is to make that cross-platform. A TUN interface is a virtual network device that appears to the operating system as a normal interface and delivers raw IP packets to and from a userspace program. What you get is the device and the plumbing to read and write packets on it. What you do not get is a VPN protocol, a routing policy, a DNS resolver or a user interface. The audience is therefore Rust developers writing software in the category of VPN clients, proxies that need to own an interface, or network diagnostic tools, and the value of the crate is that the platform-specific parts of interface creation are handled once instead of per project. The examples directory in the tree reflects that, with separate programs for reading, writing, ping, split and async codec handling.

Adding the dependency, and the feature flag that changes the API

The crate is published on crates.io as tun, and adding it to a Cargo.toml is the whole installation step:

toml
[dependencies]
tun = "0.8"

There is one decision to make at that point, and it changes the shape of the API rather than just adding a capability. If you want to use the TUN interface with mio or tokio, you enable the async feature:

toml
[dependencies]
tun = { version = "0.8", features = ["async"] }

Looking at the feature table in the packaging metadata, the async feature pulls in tokio, futures-core, futures, tokio-util and the async side of the Windows bindings, so it is not a light toggle. The default feature set is commented out in the manifest, which means nothing is enabled unless you ask, and a separate verify_binary_signature feature exists for the Windows binary, alongside the wintun-bindings feature of the same name. The crate builds as both a staticlib and a lib, which is what makes it usable as a C-compatible artefact for the mobile integrations described later.

The configuration object, and the platform_config escape hatch

Every example in the README follows the same shape. A tun::Configuration is created, its fields are set with a builder style, and tun::create turns it into a device you read from. The example sets an address, a netmask and a destination, then calls up() to bring the interface into an active state, and finally reads packets into a 4096 byte buffer in a loop. The interesting part is the platform_config hook, which is where the operating system specific settings live. On Linux, the example uses it to require root privileges so the interface gets complete functionality. On iOS, the same hook is used to turn on packet information. The design means the portable part of your code stays identical across platforms while the awkward parts are pushed into a closure that only compiles for the target you are building for. That is the pattern to follow, since platform behaviour that matters usually shows up in exactly that hook.

Permissions differ sharply between Linux, macOS and Windows

The platform notes in the README are the practical part of adopting this crate, and they differ enough to affect your design. On Linux you need the tun module to be loaded, and root is required to create interfaces. On macOS and FreeBSD, tun sets up a route according to the provided configuration, doing the equivalent of a route -n add -net 10.0.0.0/24 10.0.0.1 with sudo, so the routing comes from your configuration rather than from a separate command. On Windows you need to copy the wintun.dll file matching your architecture into the same directory as the executable, and to run the program as administrator. That Windows detail has a consequence beyond the initial setup: the DLL has to travel with your binary, so packaging and code signing have to account for it, and the verify_binary_signature feature exists to check that the binary has not been tampered with. The supported platform list is Windows, Linux, macOS, FreeBSD, Android, iOS, tvOS and OpenHarmony, all marked as supported in the README.

Mobile: passing a file descriptor instead of creating a device

On iOS and OpenHarmony the model changes completely, and this is the most interesting part of the crate. You cannot create a TUN device the way you do on a desktop, because the platform gives you the device. You pass the file descriptor of the TUN device to tun, and the crate configures an interface around it. The iOS example shows the Swift side, in a PacketTunnelProvider subclass, where setTunnelNetworkSettings is called and then the file descriptor is obtained from the packet flow before start_tun is called on a background queue, and the Rust side then uses raw_fd on the configuration and create_as_async to build the device, turning it into a framed stream of packets. The README carries a caution for iOS: the method shown for retrieving the file descriptor only works on iOS 14 and earlier, with a linked issue describing a solution for iOS 15 and later. The OpenHarmony example follows the same shape, using the vpnExtension API and a small libvpn_client.so, with the Rust side exposed to ArkTS through a napi binding.

A WTFPL licence and a small, focused dependency list

The licence is worth noticing because it is unusual. The packaging metadata declares license = "WTFPL" and the README carries a matching badge, and the repository metadata reports no standard licence identifier, which is consistent with that choice. The WTFPL is one of the most permissive licences available, with no requirement to include the licence text or to rename derived works, so for a low-level networking crate that vendors will embed in larger products, that removes friction. The repository metadata reports no standard licence identifier for the project, so the WTFPL statement in the manifest is the one to go on. The dependency list is short and mostly unconditional: bytes, cfg-if, libc with extra_traits, log, thiserror, and tokio with tokio-util as optional. The platform-specific additions are Windows with windows-sys and the wintun bindings, and ipnet on macOS and FreeBSD for the route setup. The build dependency is serde_json, used by build.rs.

Release rhythm and what the tree tells you about development

The recent releases are v0.8.11 on 2026-06-09, v0.8.12 on 2026-06-19 and v0.8.14 on 2026-07-21, and the last push was on 2026-09-26, a little under a month before this writing. The repository is not archived and the crates.io version tracks the manifest, which reads 0.8.14, with edition 2024. The version numbers below 1.0 reflect the API still moving, which is normal for a crate that wraps as many platform differences as this one does. The tree is small and legible, with a src directory, an examples directory, a Cargo.toml, a build.rs, a flake.nix and flake.lock for Nix-based development, and a .cargo directory. The examples are the documentation in practice, since the README shows only a subset, and names like dev-config.rs, ping-tun.rs, read-async-codec.rs, split-async.rs and recv-timeout.rs tell you which questions each one answers.

Editorial conclusion

tun fits a Rust developer building a VPN client, a proxy that needs to own a virtual interface, or a network tool that must read raw IP packets, and who wants one crate rather than a per-platform binding. It does not fit an application that only needs to send UDP or TCP through a proxy, since this crate gives you a device and expects you to implement the protocol on top. Before adopting it, check the platform requirements for your target, since Linux needs the tun module loaded and root, Windows needs wintun.dll beside the executable and administrator rights, and macOS and FreeBSD need route setup, and read the platform_config hook, which is where most of the platform-specific surprises live.

Frequently asked questions

How do I add the tun crate to a Rust project?

Add tun = "0.8" to the dependencies in your Cargo.toml. If you want to use the interface with mio or tokio, add the async feature instead: tun = { version = "0.8", features = ["async"] }.

What permissions does the tun crate need on each platform?

On Linux the tun module must be loaded and root is required to create interfaces. On macOS and FreeBSD the crate sets up the route from the configuration, which is equivalent to a sudo route -n add. On Windows you need wintun.dll beside the executable and must run as administrator.

How does the tun crate work on iOS and OpenHarmony?

The platform creates the device and you pass its file descriptor to the crate. The configuration uses raw_fd, and on iOS the platform_config hook enables packet information, while the OpenHarmony path uses the vpnExtension API with a small libvpn_client.so and a napi binding for ArkTS.

What licence is the tun crate released under?

The packaging metadata declares WTFPL and the README carries a matching badge, while the repository metadata field lists MIT. The crate is small, with bytes, cfg-if, libc, log and thiserror as the core dependencies, and tokio and tokio-util added only with the async feature.

Official sources

  1. Issues
  2. meh/rust-tun on GitHub
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/meh-rust-tun.svg)](https://hysenlabs.com/projects/meh-rust-tun)

Was this useful?