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"
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "p2pchat_transport_tuntapport"
|
name = "p2pchat_transport_gen_tun"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustler",
|
"rustler",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
members = ["native/p2pchat_transport_tuntapport"]
|
members = ["native/p2pchat_transport_gen_tun"]
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
elixir
|
elixir
|
||||||
elixir-ls
|
elixir-ls
|
||||||
cargo
|
cargo
|
||||||
|
rustfmt
|
||||||
rust-analyzer
|
rust-analyzer
|
||||||
rustc
|
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]
|
[package]
|
||||||
name = "p2pchat_transport_tuntapport"
|
name = "p2pchat_transport_gen_tun"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = []
|
authors = []
|
||||||
edition = "2021"
|
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