implement automatic receiving of tun messages from elixir code
This commit is contained in:
parent
7b4d355d1a
commit
486fea2088
10 changed files with 119 additions and 106 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
|
||||
members = ["native/p2pchat_transport_tuntapport"]
|
||||
members = ["native/p2pchat_transport_gen_tun"]
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
elixir
|
||||
elixir-ls
|
||||
cargo
|
||||
rustfmt
|
||||
rust-analyzer
|
||||
rustc
|
||||
];
|
||||
|
|
|
|||
52
lib/p2p_chat/transport/gen_tun.ex
Normal file
52
lib/p2p_chat/transport/gen_tun.ex
Normal file
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "p2pchat_transport_tuntapport"
|
||||
name = "p2pchat_transport_gen_tun"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
edition = "2021"
|
||||
63
native/p2pchat_transport_gen_tun/src/lib.rs
Normal file
63
native/p2pchat_transport_gen_tun/src/lib.rs
Normal 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");
|
||||
|
|
@ -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<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");
|
||||
Loading…
Add table
Add a link
Reference in a new issue