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");