yaa110 / tokio-tun Goto Github PK
View Code? Open in Web Editor NEWAsynchronous allocation of TUN/TAP devices in Rust using tokio
Home Page: https://crates.io/crates/tokio-tun
License: Apache License 2.0
Asynchronous allocation of TUN/TAP devices in Rust using tokio
Home Page: https://crates.io/crates/tokio-tun
License: Apache License 2.0
I started building a small project using this crate and quickly found that the bounds on the return type from TunBuilder::try_build
were slowly spreading throughout my project so I dove in to see why it was necessary.
I took Send + Sync
from Error
here: https://github.com/yaa110/tokio-tun/blob/master/src/result.rs#L2 and the crate builds fine. Is there a particular reason to require these traits?
My main issue is that it cannot be trivially converted to and from std::result::Result<T, Box<dyn std::error::Error>>
so it ends up spreading throughout.
If I try to return it in a function that returns Result<(), Box<dyn std::error::Error>>
, I get this error:
error[E0277]: the size for values of type `dyn std::error::Error + Sync + std::marker::Send` cannot be known at compilation time
--> src/bin/main.rs:11:45
|
11 | let mut netty = NettyStack::new(if_name)?;
| ^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `dyn std::error::Error + Sync + std::marker::Send`
= note: required because of the requirements on the impl of `std::error::Error` for `Box<dyn std::error::Error + Sync + std::marker::Send>`
= note: required because of the requirements on the impl of `From<Box<dyn std::error::Error + Sync + std::marker::Send>>` for `Box<dyn std::error::Error>`
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, Box<dyn std::error::Error + Sync + std::marker::Send>>>` for `Result<(), Box<dyn std::error::Error>>`
If I try to return Result<T, Box<dyn std::error::Error>>
in a function that returns Result<T, Box<dyn std::error::Error + Send + Sync>>
:
error[E0277]: the size for values of type `dyn std::error::Error` cannot be known at compilation time
--> src/bin/main.rs:15:74
|
15 | add_address(if_name, Ipv4Addr::new(10, 0, 0, 1).into(), handle).await?;
| ^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `dyn std::error::Error`
= note: required because of the requirements on the impl of `std::error::Error` for `Box<dyn std::error::Error>`
= note: required because of the requirements on the impl of `From<Box<dyn std::error::Error>>` for `Box<dyn std::error::Error + Sync + std::marker::Send>`
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, Box<dyn std::error::Error>>>` for `Result<(), Box<dyn std::error::Error + Sync + std::marker::Send>>`
Are there any plans to support freebsd?
Great project!
What's your plan of supporting macos and windows?
It seems that tokio-tun hangs when trying to write and read concurrently. Is this expected? Is there some pattern that allows concurrent reading and writing?
Here is a minimal example. After 5 seconds reader stops working and packet is actually never sent. Running this example with just reader or just writer (replacing reader loop with sleep) works as expected.
Tokio 1.0.1, Tokio-tun 0.3.1, Rust 1.49 stable.
use std::net::Ipv4Addr;
use std::os::unix::io::AsRawFd;
use tokio::io::AsyncReadExt;
use tokio::io::AsyncWriteExt;
use tokio_tun::result::Result;
use tokio_tun::TunBuilder;
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() -> Result<()> {
let tun = TunBuilder::new()
.name("")
.tap(false)
.packet_info(false)
.mtu(1350)
.up()
.address(Ipv4Addr::new(10, 0, 0, 1))
.destination(Ipv4Addr::new(10, 0, 0, 1))
.broadcast(Ipv4Addr::BROADCAST)
.netmask(Ipv4Addr::new(255, 0, 0, 0))
.try_build()?;
println!("-----------");
println!("tun created");
println!("-----------");
println!(
"┌ name: {}\n├ fd: {}\n├ mtu: {}\n├ flags: {}\n├ address: {}\n├ destination: {}\n├ broadcast: {}\n└ netmask: {}",
tun.name(),
tun.as_raw_fd(),
tun.mtu().unwrap(),
tun.flags().unwrap(),
tun.address().unwrap(),
tun.destination().unwrap(),
tun.broadcast().unwrap(),
tun.netmask().unwrap(),
);
println!("---------------------");
println!("ping 10.1.0.2 to test");
println!("---------------------");
let (mut reader, mut writer) = tokio::io::split(tun);
tokio::spawn( async move {
sleep(Duration::from_secs(5)).await;
let packet = [69, 0, 0, 64, 0, 0, 64, 0, 64, 6, 112, 21, 10, 0, 0, 2, 3, 209, 188, 208, 114, 4, 1, 187, 232, 33, 115, 193, 0, 0, 0, 0, 176, 2, 255, 255, 15, 234, 0, 0, 2, 4, 5, 180, 1, 3, 3, 6, 1, 1, 8, 10, 62, 53, 78, 151, 0, 0, 0, 0, 4, 2, 0, 0];
println!("Sending packet!");
writer.write(&packet).await.expect("Error!");
println!("Packet sent");
});
let mut buf = [0u8; 1024];
loop {
let n = reader.read(&mut buf).await?;
println!("reading {} bytes: {:?}", n, &buf[..n]);
}
}
I've a very simple program that creates a tun device, splits it, and writes to the write half. The write succeeds, but when I attempt to call flush it panics. Here's my sample:
#![deny(clippy::all)]
#![warn(clippy::pedantic)]
use tokio::io::AsyncWriteExt;
use std::os::fd::AsRawFd;
use tokio_tun::Tun;
use std::net::Ipv4Addr;
#[tokio::main]
async fn main() -> () {
let tun = Tun::builder()
.name("foo")
.tap(false)
.packet_info(false)
.up()
.address(Ipv4Addr::new(10, 0, 0, 1))
.destination(Ipv4Addr::new(10, 1, 0, 1))
.broadcast(Ipv4Addr::BROADCAST)
.netmask(Ipv4Addr::new(255, 255, 255, 0))
.try_build()
.unwrap();
println!("tun created, name: {}, fd: {}", tun.name(), tun.as_raw_fd());
let (_reader, mut writer) = tokio::io::split(tun);
// Just a basic ICMP request packet
let packet = [
69, 0, 0, 84, 0, 0, 64, 0, 64, 1, 38, 166, 10, 1, 0, 2, 10, 0, 0, 1,
0, 0, 102, 216, 0, 7, 5, 26, 197, 176, 29, 101, 0, 0, 0, 0, 238, 29, 4,
0, 0, 0, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55];
writer.write_all(&packet).await.unwrap();
writer.flush().await.unwrap();
}
When I invoke this small program with root privs via the following command, I generate a stack trace:
$ cargo b && sudo RUST_BACKTRACE=full ./target/debug/pinger-tokio
$ cargo b && sudo RUST_BACKTRACE=full ./target/debug/pinger-tokio
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
tun created, name: foo, fd: 9
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 22, kind: InvalidInput, message: "Invalid argument" }', src/main.rs:36:26
stack backtrace:
0: 0x559179961511 - std::backtrace_rs::backtrace::libunwind::trace::h66dc1c6acf794faa
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
1: 0x559179961511 - std::backtrace_rs::backtrace::trace_unsynchronized::ha80d20099a67f790
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
2: 0x559179961511 - std::sys_common::backtrace::_print_fmt::h7b959d43f35f16d4
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/sys_common/backtrace.rs:65:5
3: 0x559179961511 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hdaa196410d9ee0b9
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/sys_common/backtrace.rs:44:22
4: 0x559179982cef - core::fmt::rt::Argument::fmt::h0ddfbbe8be3f80d0
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/fmt/rt.rs:138:9
5: 0x559179982cef - core::fmt::write::h66b3c629f3d623e4
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/fmt/mod.rs:1094:21
6: 0x55917995e927 - std::io::Write::write_fmt::hb6d80fba4115e0c2
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/io/mod.rs:1714:15
7: 0x559179961325 - std::sys_common::backtrace::_print::h1a49cfb0cf3cce17
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/sys_common/backtrace.rs:47:5
8: 0x559179961325 - std::sys_common::backtrace::print::hca95c2d0055e42a2
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/sys_common/backtrace.rs:34:9
9: 0x559179962853 - std::panicking::default_hook::{{closure}}::hc03c01c56bca600c
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panicking.rs:269:22
10: 0x5591799625e4 - std::panicking::default_hook::hb2cb5315b6634f1c
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panicking.rs:288:9
11: 0x559179962dd9 - std::panicking::rust_panic_with_hook::h75cd912a39a34e8a
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panicking.rs:705:13
12: 0x559179962cd7 - std::panicking::begin_panic_handler::{{closure}}::h1498b46f7849e167
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panicking.rs:597:13
13: 0x559179961976 - std::sys_common::backtrace::__rust_end_short_backtrace::hd36a39b27b98086b
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/sys_common/backtrace.rs:151:18
14: 0x559179962a22 - rust_begin_unwind
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panicking.rs:593:5
15: 0x559179875273 - core::panicking::panic_fmt::h98ef273141454c23
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/panicking.rs:67:14
16: 0x559179875743 - core::result::unwrap_failed::h26e7d1a596cddd61
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/result.rs:1651:5
17: 0x55917987a2cd - core::result::Result<T,E>::unwrap::hfba736a1f516fc32
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/result.rs:1076:23
18: 0x55917987d0b2 - pinger_tokio::main::{{closure}}::hb4c29fabc0e0a84c
at /home/rjw/pinger-tokio/src/main.rs:36:5
19: 0x559179876674 - tokio::runtime::park::CachedParkThread::block_on::{{closure}}::hc2102838ad1ff373
at /home/rjw/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.32.0/src/runtime/park.rs:282:63
20: 0x5591798764a5 - tokio::runtime::coop::with_budget::h7bd2944232a4b354
at /home/rjw/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.32.0/src/runtime/coop.rs:107:5
21: 0x5591798764a5 - tokio::runtime::coop::budget::h35f9a719fa4c7073
at /home/rjw/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.32.0/src/runtime/coop.rs:73:5
22: 0x5591798764a5 - tokio::runtime::park::CachedParkThread::block_on::hdd97a7aecde53be4
at /home/rjw/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.32.0/src/runtime/park.rs:282:31
23: 0x55917987c2e5 - tokio::runtime::context::blocking::BlockingRegionGuard::block_on::h8bcad03ed3746a7a
at /home/rjw/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.32.0/src/runtime/context/blocking.rs:66:9
24: 0x55917987c056 - tokio::runtime::scheduler::multi_thread::MultiThread::block_on::{{closure}}::hdc0db8febb28906c
at /home/rjw/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.32.0/src/runtime/scheduler/multi_thread/mod.rs:87:13
25: 0x55917987b8fc - tokio::runtime::context::runtime::enter_runtime::h83da395e19220ae7
at /home/rjw/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.32.0/src/runtime/context/runtime.rs:65:16
26: 0x55917987c011 - tokio::runtime::scheduler::multi_thread::MultiThread::block_on::h0b01f4b37ea5a961
at /home/rjw/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.32.0/src/runtime/scheduler/multi_thread/mod.rs:86:9
27: 0x5591798767cb - tokio::runtime::runtime::Runtime::block_on::hc28efc14991c6fd4
at /home/rjw/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.32.0/src/runtime/runtime.rs:349:45
28: 0x55917987a9fd - pinger_tokio::main::h5657d9d59fbfb77f
at /home/rjw/pinger-tokio/src/main.rs:36:5
29: 0x55917987719b - core::ops::function::FnOnce::call_once::h9646c2d630b860c2
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/ops/function.rs:250:5
30: 0x55917987c5de - std::sys_common::backtrace::__rust_begin_short_backtrace::h574604846fa0a270
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/sys_common/backtrace.rs:135:18
31: 0x559179877a11 - std::rt::lang_start::{{closure}}::he00ac19dd4a14f13
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/rt.rs:166:18
32: 0x55917995b3eb - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::h87e9a912d8a0de33
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/ops/function.rs:284:13
33: 0x55917995b3eb - std::panicking::try::do_call::ha8d57d42181c12cf
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panicking.rs:500:40
34: 0x55917995b3eb - std::panicking::try::h2fdb2d19c253437c
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panicking.rs:464:19
35: 0x55917995b3eb - std::panic::catch_unwind::h9fe5959d2e133449
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panic.rs:142:14
36: 0x55917995b3eb - std::rt::lang_start_internal::{{closure}}::ha072eb7bbeaa0b6a
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/rt.rs:148:48
37: 0x55917995b3eb - std::panicking::try::do_call::h983dae29f79ed5db
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panicking.rs:500:40
38: 0x55917995b3eb - std::panicking::try::h15014751f4b412ba
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panicking.rs:464:19
39: 0x55917995b3eb - std::panic::catch_unwind::hf837426183b1055f
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panic.rs:142:14
40: 0x55917995b3eb - std::rt::lang_start_internal::h2bbe0b58b2b89a9f
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/rt.rs:148:20
41: 0x5591798779ea - std::rt::lang_start::hf6639bc367ab8881
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/rt.rs:165:17
42: 0x55917987aaae - main
43: 0x7f4c90c900d0 - <unknown>
44: 0x7f4c90c90189 - __libc_start_main
45: 0x559179875a45 - _start
46: 0x0 - <unknown>
I'd like to note that if I use async-tun, I can write to the Tun device fine. The difference may be in the tokio::split vs. Tun.writer() calls, I'm not sure. Thought, it appears this is not the case, as changing the sample program above to forego calling tokio::io::split and instead just writing and flushing the Tun device directly produces the same result. e.g.,
// Just a basic ICMP request packet
let packet = [
69, 0, 0, 84, 0, 0, 64, 0, 64, 1, 38, 166, 10, 1, 0, 2, 10, 0, 0, 1,
0, 0, 102, 216, 0, 7, 5, 26, 197, 176, 29, 101, 0, 0, 0, 0, 238, 29, 4,
0, 0, 0, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55];
tun.write_all(&packet).await.unwrap();
tun.flush().await.unwrap();
I should also note my platform info:
$ rustup show
Default host: x86_64-unknown-linux-gnu
rustup home: /home/rjw/.rustup
installed toolchains
--------------------
stable-x86_64-unknown-linux-gnu (default)
nightly-x86_64-unknown-linux-gnu
active toolchain
----------------
stable-x86_64-unknown-linux-gnu (default)
rustc 1.72.1 (d5c2e9c34 2023-09-13)
$ uname -a Linux angband 5.15.90.1-microsoft-standard-WSL2 #1 SMP Fri Jan 27 02:56:13 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
As you can see, I am running on Ubuntu under WSL. I'm not sure this is relevant but, one never knows.
Tun
implements AsRawFd
but it would be useful to support FromRawFd
or moral equivalent.
The use case is if you inherit a fd for a tun device or receive it from another process, but want to use it in an async context with tokio-tun instead of treating it as a standard file.
Obviously in this case you do not have a handle to the tun interface description and can't get MTU/address etc. which prevents a trivial implementation of FromRawFd
.
It would be nice if a user could choose whether they want to use IPv4 or IPv6 on their TUN device.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.