From 486fea2088eb58a6e1be949f12e6fe67a5f511bd Mon Sep 17 00:00:00 2001 From: lilly Date: Thu, 21 May 2026 13:59:10 +0200 Subject: [PATCH] implement automatic receiving of tun messages from elixir code --- Cargo.lock | 2 +- Cargo.toml | 2 +- flake.nix | 1 + lib/p2p_chat/transport/gen_tun.ex | 52 +++++++++++++++ lib/p2p_chat/transport/tuntap.ex | 33 ---------- lib/p2p_chat/transport/tuntapport.ex | 8 --- .../Cargo.toml | 2 +- .../README.md | 0 native/p2pchat_transport_gen_tun/src/lib.rs | 63 +++++++++++++++++++ .../p2pchat_transport_tuntapport/src/lib.rs | 62 ------------------ 10 files changed, 119 insertions(+), 106 deletions(-) create mode 100644 lib/p2p_chat/transport/gen_tun.ex delete mode 100644 lib/p2p_chat/transport/tuntap.ex delete mode 100644 lib/p2p_chat/transport/tuntapport.ex rename native/{p2pchat_transport_tuntapport => p2pchat_transport_gen_tun}/Cargo.toml (85%) rename native/{p2pchat_transport_tuntapport => p2pchat_transport_gen_tun}/README.md (100%) create mode 100644 native/p2pchat_transport_gen_tun/src/lib.rs delete mode 100644 native/p2pchat_transport_tuntapport/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index d24300e..796b5da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -407,7 +407,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] -name = "p2pchat_transport_tuntapport" +name = "p2pchat_transport_gen_tun" version = "0.1.0" dependencies = [ "rustler", diff --git a/Cargo.toml b/Cargo.toml index b9393f5..28fe060 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ [workspace] resolver = "2" -members = ["native/p2pchat_transport_tuntapport"] +members = ["native/p2pchat_transport_gen_tun"] diff --git a/flake.nix b/flake.nix index b06ee2d..2bd11e9 100644 --- a/flake.nix +++ b/flake.nix @@ -27,6 +27,7 @@ elixir elixir-ls cargo + rustfmt rust-analyzer rustc ]; diff --git a/lib/p2p_chat/transport/gen_tun.ex b/lib/p2p_chat/transport/gen_tun.ex new file mode 100644 index 0000000..98113a3 --- /dev/null +++ b/lib/p2p_chat/transport/gen_tun.ex @@ -0,0 +1,52 @@ +defmodule P2pChat.Transport.GenTun do + require Logger + use Rustler, otp_app: :p2p_chat, crate: "p2pchat_transport_gen_tun" + + @behaviour GenServer + + defstruct [:tun_handle] + + # + # Server API + # + @impl true + def init(args) do + {:ok, tun_handle} = make_tun_device() + state = %__MODULE__{tun_handle: tun_handle} + { :ok, state, { :continue, :recv } } + end + + @impl true + def handle_continue(:recv, state) do + case tun_recv(state.tun_handle) do + {:ok, buf} -> + Logger.debug("Received #{byte_size(buf)} bytes") + {:error, :would_block} -> {} + {:error, e} -> + Logger.error("Error during receive: #{e}") + end + + {:noreply, state, { :continue, :recv }} + end + + @impl true + def handle_call({:recv}, _from, state) do + data = tun_recv(state.tun_handle) + { :reply, data, state } + end + + # + # Client API + # + def open() do + GenServer.start_link(__MODULE__, nil) + end + + # + # NIFs + # + + defp make_tun_device(), do: :erlang.nif_error(:nif_not_loaded) + defp tun_recv(_handle, _bufsize \\ 2*16), do: :erlang.nif_error(:nif_not_loaded) +end + diff --git a/lib/p2p_chat/transport/tuntap.ex b/lib/p2p_chat/transport/tuntap.ex deleted file mode 100644 index b5f0a24..0000000 --- a/lib/p2p_chat/transport/tuntap.ex +++ /dev/null @@ -1,33 +0,0 @@ -defmodule P2pChat.Transport.TunTap do - use GenServer - - defstruct [:tun_fd] - - @impl true - def init(args) do - {:ok, io_device} = setup_tap_device() - {:ok, {}} - end - - @impl true - def terminate(_reason, state) do - - end - - def setup_tap_device(name \\ "tunP2P") do - {:ok, io_device} = :file.open(~c"/dev/net/tun", [:read, :write, :raw]) - :ok = :file.close(io_device) - :ok - end - - def teardown_tap_device(io_device) do - end - - # - # Client API - # - - def start_link(args) do - GenServer.start_link(__MODULE__, args) - end -end diff --git a/lib/p2p_chat/transport/tuntapport.ex b/lib/p2p_chat/transport/tuntapport.ex deleted file mode 100644 index cc992ae..0000000 --- a/lib/p2p_chat/transport/tuntapport.ex +++ /dev/null @@ -1,8 +0,0 @@ -defmodule P2pChat.Transport.TunTapPort do - use Rustler, otp_app: :p2p_chat, crate: "p2pchat_transport_tuntapport" - - def make_tun_device(), do: :erlang.nif_error(:nif_not_loaded) - - def read(_handle, _length \\ 2**16), do: :erlang.nif_error(:nif_not_loaded) -end - diff --git a/native/p2pchat_transport_tuntapport/Cargo.toml b/native/p2pchat_transport_gen_tun/Cargo.toml similarity index 85% rename from native/p2pchat_transport_tuntapport/Cargo.toml rename to native/p2pchat_transport_gen_tun/Cargo.toml index c13d7f3..e7a6510 100644 --- a/native/p2pchat_transport_tuntapport/Cargo.toml +++ b/native/p2pchat_transport_gen_tun/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "p2pchat_transport_tuntapport" +name = "p2pchat_transport_gen_tun" version = "0.1.0" authors = [] edition = "2021" diff --git a/native/p2pchat_transport_tuntapport/README.md b/native/p2pchat_transport_gen_tun/README.md similarity index 100% rename from native/p2pchat_transport_tuntapport/README.md rename to native/p2pchat_transport_gen_tun/README.md diff --git a/native/p2pchat_transport_gen_tun/src/lib.rs b/native/p2pchat_transport_gen_tun/src/lib.rs new file mode 100644 index 0000000..bcd3d31 --- /dev/null +++ b/native/p2pchat_transport_gen_tun/src/lib.rs @@ -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 { + 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, bufsize: usize) -> NifResult { + 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(env: rustler::Env, value: T) -> Term { + make_tuple(env, &[atoms::ok().encode(env), value.encode(env)]) +} + +rustler::init!("Elixir.P2pChat.Transport.GenTun"); diff --git a/native/p2pchat_transport_tuntapport/src/lib.rs b/native/p2pchat_transport_tuntapport/src/lib.rs deleted file mode 100644 index b1b781c..0000000 --- a/native/p2pchat_transport_tuntapport/src/lib.rs +++ /dev/null @@ -1,62 +0,0 @@ -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> { - 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, length: usize) -> NifResult { - 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");