implement automatic receiving of tun messages from elixir code

This commit is contained in:
lilly 2026-05-21 13:59:10 +02:00
commit 486fea2088
Signed by: lilly
SSH key fingerprint: SHA256:y9T5GFw2A20WVklhetIxG1+kcg/Ce0shnQmbu1LQ37g
10 changed files with 119 additions and 106 deletions

View file

@ -0,0 +1,13 @@
[package]
name = "p2pchat_transport_gen_tun"
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,63 @@
use std::io;
use std::net::Ipv6Addr;
use rustler::types::tuple::make_tuple;
use rustler::{resource_impl, Encoder, NifResult, OwnedBinary, Resource, ResourceArc, Term};
use tun_rs::{DeviceBuilder, SyncDevice};
mod atoms {
rustler::atoms! {
ok,
resource_busy,
permission_denied,
would_block,
}
}
pub struct TunHandle {
device: SyncDevice,
}
#[resource_impl]
impl Resource for TunHandle {}
#[rustler::nif]
fn make_tun_device(env: rustler::Env) -> NifResult<Term> {
let device = DeviceBuilder::new()
.name("tunP2P")
.ipv6(Ipv6Addr::new(0x2001, 0x2f, 0, 0, 0, 0, 0, 1), 28)
.packet_information(true)
.build_sync()
.map_err(map_io_error)?;
device.set_nonblocking(true).map_err(map_io_error)?;
let handle = ResourceArc::new(TunHandle { device });
Ok(make_ok_tuple(env, handle))
}
#[rustler::nif]
fn tun_recv(env: rustler::Env, handle: ResourceArc<TunHandle>, bufsize: usize) -> NifResult<Term> {
let mut buf = OwnedBinary::new(bufsize).expect("Could not allocate receive buffer");
let n = handle.device.recv(&mut buf).map_err(map_io_error)?;
assert!(buf.realloc(n));
let erl_buf = buf.release(env);
Ok(make_ok_tuple(env, erl_buf))
}
#[inline]
fn map_io_error(error: io::Error) -> rustler::Error {
rustler::Error::Term(match error.kind() {
io::ErrorKind::ResourceBusy => Box::new(atoms::resource_busy()),
io::ErrorKind::PermissionDenied => Box::new(atoms::permission_denied()),
io::ErrorKind::WouldBlock => Box::new(atoms::would_block()),
e => Box::new(e.to_string()),
})
}
#[inline]
fn make_ok_tuple<T: Encoder>(env: rustler::Env, value: T) -> Term {
make_tuple(env, &[atoms::ok().encode(env), value.encode(env)])
}
rustler::init!("Elixir.P2pChat.Transport.GenTun");