implement minimal NIF to create and read from a TUN device

This commit is contained in:
lilly 2026-05-20 18:00:28 +02:00
commit 7b4d355d1a
Signed by: lilly
SSH key fingerprint: SHA256:y9T5GFw2A20WVklhetIxG1+kcg/Ce0shnQmbu1LQ37g
12 changed files with 963 additions and 1 deletions

View file

@ -0,0 +1,13 @@
[package]
name = "p2pchat_transport_tuntapport"
version = "0.1.0"
authors = []
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
rustler = "0.37.4"
tun-rs = { version = "2.8.3", default-features = false, features = ["blocking", "experimental"] }

View file

@ -0,0 +1,20 @@
# NIF for P2pChat.Transport.TunTapPort
## To build the NIF module:
- Your NIF will now build along with your project.
## To load the NIF:
```elixir
defmodule P2pChat.Transport.TunTapPort do
use Rustler, otp_app: :p2p_chat, crate: "p2pchat_transport_tuntapport"
# When your NIF is loaded, it will override this function.
def add(_a, _b), do: :erlang.nif_error(:nif_not_loaded)
end
```
## Examples
[This](https://github.com/rusterlium/NifIo) is a complete example of a NIF written in Rust.

View file

@ -0,0 +1,62 @@
use std::io;
use std::net::Ipv6Addr;
use rustler::Binary;
use rustler::NifResult;
use rustler::OwnedBinary;
use rustler::Resource;
use rustler::ResourceArc;
use rustler::Error;
use tun_rs::DeviceBuilder;
use tun_rs::SyncDevice;
mod atoms {
rustler::atoms! {
ok,
would_block,
other_error,
}
}
pub struct TunHandle {
device: SyncDevice,
}
#[rustler::resource_impl]
impl Resource for TunHandle {}
#[rustler::nif]
fn make_tun_device() -> NifResult<ResourceArc<TunHandle>> {
let device = DeviceBuilder::new()
.name("tunP2P")
.ipv6(Ipv6Addr::new(0x2001, 0x2f, 0, 0, 0, 0, 0, 1), 28)
.build_sync()
.expect("Could not create tun device");
device.set_nonblocking(true).expect("Could not make TUN device nonblocking");
println!("TUN device {} created", device.name().unwrap());
let handle = ResourceArc::new(TunHandle { device });
Ok(handle)
}
#[rustler::nif]
fn read(env: rustler::Env, handle: ResourceArc<TunHandle>, length: usize) -> NifResult<Binary> {
let mut buf = OwnedBinary::new(length).expect("Could not allocate a buffer from the BEAM");
let n = match handle.device.recv(&mut buf) {
Ok(n) => n,
Err(e) => return match e.kind() {
io::ErrorKind::WouldBlock => Err(Error::Term(Box::new(atoms::would_block()))),
_ => Err(Error::Term(Box::new(atoms::other_error()))),
},
};
println!("Have read {n} bytes");
let erl_buf = Binary::from_owned(buf, env);
Ok(erl_buf)
}
rustler::init!("Elixir.P2pChat.Transport.TunTapPort");