implement a bunch of utility functions on tun device primitive
This commit is contained in:
parent
486fea2088
commit
d94f20d042
9 changed files with 248 additions and 82 deletions
215
native/p2pchat_transport_prim_tun/src/lib.rs
Normal file
215
native/p2pchat_transport_prim_tun/src/lib.rs
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
use std::io;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use std::ops::Deref;
|
||||
|
||||
use rustler::types::tuple::{get_tuple, make_tuple};
|
||||
use rustler::{
|
||||
resource_impl, Binary, Decoder, Encoder, NifResult, OwnedBinary, Resource, ResourceArc, Term,
|
||||
};
|
||||
use tun_rs::{DeviceBuilder, SyncDevice};
|
||||
|
||||
mod atoms {
|
||||
rustler::atoms! {
|
||||
ok,
|
||||
resource_busy,
|
||||
permission_denied,
|
||||
would_block,
|
||||
address_not_available,
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaque handle which the BEAM an use to refer to an open TUN Device
|
||||
pub struct TunHandle {
|
||||
device: SyncDevice,
|
||||
}
|
||||
|
||||
/// A utility wrapper around native IP addresses that has BEAM encoding/decoding rules implemented
|
||||
#[repr(transparent)]
|
||||
struct WrappedIpAddr(IpAddr);
|
||||
|
||||
impl From<IpAddr> for WrappedIpAddr {
|
||||
fn from(value: IpAddr) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for WrappedIpAddr {
|
||||
type Target = IpAddr;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder for WrappedIpAddr {
|
||||
fn encode<'a>(&self, env: rustler::Env<'a>) -> Term<'a> {
|
||||
match self.0 {
|
||||
IpAddr::V6(addr) => {
|
||||
let octets = addr
|
||||
.segments()
|
||||
.into_iter()
|
||||
.map(|i| i.encode(env))
|
||||
.collect::<Vec<_>>();
|
||||
make_tuple(env, &octets)
|
||||
}
|
||||
IpAddr::V4(addr) => {
|
||||
let octets = addr.octets();
|
||||
(octets[0], octets[1], octets[2], octets[3]).encode(env)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Decoder<'a> for WrappedIpAddr {
|
||||
fn decode(term: Term<'a>) -> NifResult<Self> {
|
||||
let term_tuple = get_tuple(term)?;
|
||||
match term_tuple.len() {
|
||||
8 => {
|
||||
let segments: [u16; 8] = [
|
||||
term_tuple[0].decode()?,
|
||||
term_tuple[1].decode()?,
|
||||
term_tuple[2].decode()?,
|
||||
term_tuple[3].decode()?,
|
||||
term_tuple[4].decode()?,
|
||||
term_tuple[5].decode()?,
|
||||
term_tuple[6].decode()?,
|
||||
term_tuple[7].decode()?,
|
||||
];
|
||||
Ok(IpAddr::V6(Ipv6Addr::from_segments(segments)).into())
|
||||
}
|
||||
4 => {
|
||||
let octets: [u8; 4] = [
|
||||
term_tuple[0].decode()?,
|
||||
term_tuple[1].decode()?,
|
||||
term_tuple[2].decode()?,
|
||||
term_tuple[3].decode()?,
|
||||
];
|
||||
Ok(IpAddr::V4(Ipv4Addr::from_octets(octets)).into())
|
||||
}
|
||||
_ => Err(rustler::Error::BadArg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[resource_impl]
|
||||
impl Resource for TunHandle {}
|
||||
|
||||
#[rustler::nif]
|
||||
fn make_tun_device(env: rustler::Env, packet_info: bool) -> NifResult<Term> {
|
||||
let device = DeviceBuilder::new()
|
||||
.name("tunP2P")
|
||||
.packet_information(packet_info)
|
||||
.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 recv(env: rustler::Env, handle: ResourceArc<TunHandle>, bufsize: usize) -> NifResult<Term> {
|
||||
handle.device.set_nonblocking(true).map_err(map_io_error)?;
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn send(handle: ResourceArc<TunHandle>, buf: Binary) -> NifResult<()> {
|
||||
handle.device.set_nonblocking(false).map_err(map_io_error)?;
|
||||
|
||||
let mut n = 0;
|
||||
while n < buf.len() {
|
||||
n += handle.device.send(&buf[n..]).map_err(map_io_error)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn get_addrs(handle: ResourceArc<TunHandle>) -> NifResult<Vec<WrappedIpAddr>> {
|
||||
let addrs = handle.device.addresses().map_err(map_io_error)?;
|
||||
Ok(addrs.into_iter().map(|i| i.into()).collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn get_broadcast(handle: ResourceArc<TunHandle>) -> NifResult<WrappedIpAddr> {
|
||||
let broadcast = handle.device.broadcast().map_err(map_io_error)?;
|
||||
Ok(broadcast.into())
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn get_name(handle: ResourceArc<TunHandle>) -> NifResult<String> {
|
||||
handle.device.name().map_err(map_io_error)
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn set_name(handle: ResourceArc<TunHandle>, value: &str) -> NifResult<()> {
|
||||
handle.device.set_name(value).map_err(map_io_error)
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn is_running(handle: ResourceArc<TunHandle>) -> NifResult<bool> {
|
||||
handle.device.is_running().map_err(map_io_error)
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn set_running(handle: ResourceArc<TunHandle>, should_run: bool) -> NifResult<()> {
|
||||
handle.device.enabled(should_run).map_err(map_io_error)
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn add_address(
|
||||
handle: ResourceArc<TunHandle>,
|
||||
addr: WrappedIpAddr,
|
||||
prefix_length: u8,
|
||||
) -> NifResult<()> {
|
||||
match addr.0 {
|
||||
IpAddr::V6(addr) => handle
|
||||
.device
|
||||
.add_address_v6(addr, prefix_length)
|
||||
.map_err(map_io_error)?,
|
||||
IpAddr::V4(addr) => handle
|
||||
.device
|
||||
.add_address_v4(addr, prefix_length)
|
||||
.map_err(map_io_error)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn remove_address(handle: ResourceArc<TunHandle>, addr: WrappedIpAddr) -> NifResult<()> {
|
||||
handle.device.remove_address(*addr).map_err(map_io_error)
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn get_mtu(handle: ResourceArc<TunHandle>) -> NifResult<u16> {
|
||||
handle.device.mtu().map_err(map_io_error)
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn set_mtu(handle: ResourceArc<TunHandle>, mtu: u16) -> NifResult<()> {
|
||||
handle.device.set_mtu(mtu).map_err(map_io_error)
|
||||
}
|
||||
|
||||
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()),
|
||||
io::ErrorKind::AddrNotAvailable => Box::new(atoms::address_not_available()),
|
||||
e => Box::new(e.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
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.PrimTun");
|
||||
Loading…
Add table
Add a link
Reference in a new issue