meh / rust-tun Goto Github PK
View Code? Open in Web Editor NEWTUN device creation and handling.
TUN device creation and handling.
On linux, packet_information
default value is false, which means IFF_NO_PI will be used. But on macos, the 4-byte ethernet family header will be passed when read.
I think the behavior should be consistent on different platforms. Should the behavior on macos changed? Maybe packet_information
can be applied for macos too to control whether remove the header?
I've seen you are going to do also TAP crate.
Does it have any ETA?
I would like to use it.
os: macos m1
let mut tun_config = tun::Configuration::default();
tun_config.address((10, 0, 0, 1)).netmask((255, 255, 255, 0)).up();
# netstat -nr -f inet
Routing tables
Internet:
Destination Gateway Flags Netif Expire
10.0.0.255 10.0.0.1 UH utun14
let mut tun_config = tun::Configuration::default();
tun_config.address((10, 0, 1, 1)).netmask((255, 255, 255, 0)).up();
# netstat -nr -f inet
Routing tables
Internet:
Destination Gateway Flags Netif Expire
10.0.0.255 10.0.1.1 UH utun14
I think corret route table is 10.0.1.255, but it's not change.
Hello. I used the following code to set up a simple tun device and read from it:
`let mut config = tun::Configuration::default();
config.address((10, 2, 0, 1))
.netmask((255, 255, 255, 0))
.up();
#[cfg(target_os = "linux")]
config.platform(|config| {
config.packet_information(true);
});
let mut dev = tun::create(&config).unwrap();
let mut buf = [0; 4096];
loop {
let amount = dev.read(&mut buf).unwrap();
println!("{:?}", &buf[0 .. amount]);
}`
As you can see it is just the example code from the repository. of course i imported everything correctly. I don't get an error and it seems to work at first but nothing happens when i send a packet to the ip address of the interface.
Running ipconfig getiflist
also doesn't show a utun device. I am using macOS 13.1 Ventuara btw.
Thank you for your help.
i'm on windows 10 and i'm using following toolchain
> rustup show
Default host: x86_64-pc-windows-msvc
rustup home: C:\Users\ASUS\.rustup
installed toolchains
--------------------
stable-x86_64-pc-windows-gnu
stable-x86_64-pc-windows-msvc (default)
installed targets for active toolchain
--------------------------------------
x86_64-pc-windows-msvc
x86_64-unknown-linux-gnu
x86_64-unknown-linux-musl
active toolchain
----------------
stable-x86_64-pc-windows-msvc (default)
rustc 1.69.0 (84c898d65 2023-04-16)
the error is
> cargo check
Checking tun v0.5.5
error[E0433]: failed to resolve: could not find `unix` in `os`
--> C:\Users\ASUS\.cargo\registry\src\github.com-1ecc6299db9ec823\tun-0.5.5\src\configuration.rs:16:14
|
16 | use std::os::unix::io::RawFd;
| ^^^^ could not find `unix` in `os`
error[E0432]: unresolved import `crate::platform::create`
--> C:\Users\ASUS\.cargo\registry\src\github.com-1ecc6299db9ec823\tun-0.5.5\src\lib.rs:28:9
|
28 | pub use crate::platform::create;
| ^^^^^^^^^^^^^^^^^^^^^^^ no `create` in `platform`
error[E0412]: cannot find type `Configuration` in module `platform`
--> C:\Users\ASUS\.cargo\registry\src\github.com-1ecc6299db9ec823\tun-0.5.5\src\configuration.rs:38:36
|
38 | pub(crate) platform: platform::Configuration,
| ^^^^^^^^^^^^^ not found in `platform`
|
help: consider importing this struct
|
15 | use crate::Configuration;
|
help: if you import `Configuration`, refer to it directly
|
38 - pub(crate) platform: platform::Configuration,
38 + pub(crate) platform: Configuration,
|
error[E0412]: cannot find type `Configuration` in module `platform`
--> C:\Users\ASUS\.cargo\registry\src\github.com-1ecc6299db9ec823\tun-0.5.5\src\configuration.rs:55:34
|
55 | F: FnOnce(&mut platform::Configuration),
| ^^^^^^^^^^^^^ not found in `platform`
|
help: consider importing this struct
|
15 | use crate::Configuration;
|
help: if you import `Configuration`, refer to it directly
|
55 - F: FnOnce(&mut platform::Configuration),
55 + F: FnOnce(&mut Configuration),
|
error[E0618]: expected function, found `F`
--> C:\Users\ASUS\.cargo\registry\src\github.com-1ecc6299db9ec823\tun-0.5.5\src\configuration.rs:57:9
|
53 | pub fn platform<F>(&mut self, f: F) -> &mut Self
| - `f` has type `F`
...
57 | f(&mut self.platform);
| ^--------------------
| |
| call expression requires function
Some errors have detailed explanations: E0412, E0432, E0433, E0618.
For more information about an error, try `rustc --explain E0412`.
error: could not compile `tun` due to 5 previous errors
and the Cargo.toml file
[package]
name = "rust-test-3"
version = "0.1.0"
edition = "2021"
[dependencies]
tun = "0.5.5"
Is there any plan to supported tokio 1.x ?
Hi,
raw_fd created by Android VpnService establish()
, pass raw_fd to rust tun, When shutdown tun, drop fd libc::close
invoked automatically, and Android 11 Crashed
A/libc: fdsan: attempted to close file descriptor 68, expected to be unowned, actually owned by ParcelFileDescriptor 0xa381657
it's not allowed close
with no tag in Android 11 or higher.
add a option to close raw_fd manually: PR HERE
ref: fdsan, android_fdsan_close_with_tag and close
Is there a way to enable TSO?
My ethernet card has it enabled by default:
...while the TUN adapter created via this crate has not:
The ethtool
command just says that it was '[requested on]' but in practice it's not active.
Is this a known problem?
I couldn't find anything in the docs/issues.
Thanks in advance for your time.
Mac OSX 10.5.14
Cargo.toml
...
rust-tun ={ version = "0.5.0", , features = ["async"] }
futures = "0.3.1"
tokio = { version = "0.2", features = ["full"]}
tokio-util = { version = "0.3", features = ["codec", "udp"]}
...
thread 'main' panicked at 'split_to out of bounds: 4 <= 1', /Users/user/.cargo/registry/src/github.com-1ecc6299db9ec823/bytes-0.5.4/src/bytes_mut.rs:352:9
stack backtrace:
0: backtrace::backtrace::libunwind::trace
at /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/libunwind.rs:88
1: backtrace::backtrace::trace_unsynchronized
at /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/mod.rs:66
2: std::sys_common::backtrace::_print_fmt
at src/libstd/sys_common/backtrace.rs:84
3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
at src/libstd/sys_common/backtrace.rs:61
4: core::fmt::write
at src/libcore/fmt/mod.rs:1025
5: std::io::Write::write_fmt
at src/libstd/io/mod.rs:1426
6: std::sys_common::backtrace::_print
at src/libstd/sys_common/backtrace.rs:65
7: std::sys_common::backtrace::print
at src/libstd/sys_common/backtrace.rs:50
8: std::panicking::default_hook::{{closure}}
at src/libstd/panicking.rs:193
9: std::panicking::default_hook
at src/libstd/panicking.rs:210
10: std::panicking::rust_panic_with_hook
at src/libstd/panicking.rs:471
11: rust_begin_unwind
at src/libstd/panicking.rs:375
12: core::panicking::panic_fmt
at src/libcore/panicking.rs:84
13: bytes::bytes_mut::BytesMut::split_to
at /Users/user/.cargo/registry/src/github.com-1ecc6299db9ec823/bytes-0.5.4/src/bytes_mut.rs:352
14: <tun::async::codec::TunPacketCodec as tokio_util::codec::decoder::Decoder>::decode
at /Users/user/CLionProjects/rust-tun/src/async/codec.rs:106
15: <tokio_util::codec::framed_read::FramedRead2<T> as futures_core::stream::Stream>::poll_next
at /Users/user/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-util-0.3.1/src/codec/framed_read.rs:269
16: <tokio_util::codec::framed::Framed<T,U> as futures_core::stream::Stream>::poll_next
at /Users/user/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-util-0.3.1/src/codec/framed.rs:257
17: <futures_util::stream::stream::split::SplitStream<S> as futures_core::stream::Stream>::poll_next
at /Users/user/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.5/src/stream/stream/split.rs:31
18: futures_util::stream::stream::StreamExt::poll_next_unpin
at /Users/user/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.5/src/stream/stream/mod.rs:1323
19: <futures_util::stream::stream::next::Next<St> as core::future::future::Future>::poll
at /Users/user/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.5/src/stream/stream/next.rs:35
20: std::future::poll_with_tls_context
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/future.rs:99
...
When I was doing something like this
let framed: FramedTunQueue = tun_queue.into_framed();
let (mut tun_sink, mut tun_stream) = framed.split();
while let Some(tun_pkt) = tun_stream.next().await {... // panic at this line
Currently, with high throughput workloads, the Linux implementation struggles to get beyond ~ 1.3 Gbps of throughput (could be marginally more with nothing but rust-tun).
This could be improved by implementing GRO and GSO for TCP and UDP packets, which has been added and benchmarked by Tailscale in the TUN driver of wireguard-go, leading to significant improvements in throughput, as described in the following articles:
This would be a non-trivial change, but I might take it on later this year, as I would really love to use it in quincy, which uses rust-tun.
This relates to M0dEx/quincy#10
How do I do full-duplex communication using Device
without using AsRawFd
, just with std::io::Read
and std::io::Write
?
Maybe Device
should have try_clone()
, so both copies can be sent to separate threads?
Code pointer:
https://github.com/meh/rust-tun/blob/e762c419d71438e67379f9b2fed89b5cb835cc6a/src/async/codec.rs#L77C1-L80C2
TunPacket
already stores the packet internally using bytes::Bytes
, but it can only be constructed from a Vec
via TunPacket::new
. It would be nice to have a way (either a method of a From
trait impl) to construct a TunPacket
from Bytes
directly, without going through the extra cloning in Bytes -> Vec -> Bytes
.
First of all, thank you for using your valuable experience to see my request!
I want to use two threads to handle reads and writes separately, but I haven't found any good methods other than using read write locks. The read write lock, due to its ability to block, has not yet achieved the goal of simultaneously reading and writing separately.
Is there any solution?
Any plan or timeline to support windows platform?
Hello
As mentioned in #4, I just created a „competing“ crate, whithout the intention to compete with anyone. I have a minimal implementation for async with tokio, which I wanted to extend in the future. It works by turning the interface object into an async object (which no longer supports all the fancy things like getting the name) that implements Sink
and Stream
.
So, I was thinking, instead of trying to compete, would it make sense if I ported that async wrapper to this crate?
I don't exactly get how to do routing such that a VPN style system would work (all traffic comes into TUN and exits via another interface). I generally get how to use the library to make an interface and receive packets, but sending them out when TUN is the default gateway (and making it the default gateway in the first place) is beyond me.
Hello
I just needed a TAP device crate and only after writing one myself, I discovered this, which seems to be richer in features than mine (mine is just raw pipe for packets, without any configuration of the interface). However, reading the documentation, it seems this completely lacks any IPv6 consideration.
Is it planned or is that on purpose?
OS: Ubuntu 20.04.3 LTS (Linux ubuntu 5.11.0-34-generic #36~20.04.1-Ubuntu SMP Fri Aug 27 08:06:32 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
)
It seems that on Linux, you can't use sendmsg
and recvmsg
on a tun fd.
rust-tun/src/platform/posix/fd.rs
Line 99 in aeca1a6
Reproduced by using https://github.com/meh/rust-tun/blob/master/examples/ping-tun.rs
When I ping 10.0.0.2
, that program gets an ICMP packet, and it tries to reply. At that point it fails in https://github.com/meh/rust-tun/blob/master/examples/ping-tun.rs#L66 with code 88
, Socket operation on non-socket
This is happening on Linux Ubuntu 20.04 with Kernel 5.13.0-44 , in case that is relevant for reproducibility.
Hey guys,
it's good to have multiqueue support now, but it breaks trait IntoRawFd
for Device
.
see here https://github.com/meh/rust-tun/blob/master/src/platform/linux/device.rs#L381
I think it may cause some trouble if someone calls into_raw_fd()
to transfer the ownership of RawFd
, cause when Device
is dropped, the underlying fd will be closed.
Sorry for the odd little, but I ran into the odd issue myself. I created a TUN interface and tried netcat on it. But when I close my netcat, I still see it is receiving packets. Weirdly, if I keep nc running, it doesn't receive any!
Details:
rust-tun version: 0.5.1
OS, version: Mac OS Catalina, 10.15.7 (19H2)
rust: rustc 1.50.0 (cb75ad5db 2021-02-10)
My reproducible code is here - main.rs. Once this is running, it replies the TCP packet only once and then it just keeps logging whatever the data it has received. So, first packet it gets is TCP SYN, to which it responds with TCP ACK and then logs rest of the packets to console.
Steps to reproduce:
Build and run it:
cargo build --release
sudo ./target/release/testing-tun
Setup the links:
sudo ifconfig utun4 192.168.0.10 192.168.0.20 up
start netcat:
nc 192.168.0.20 80
If you don't close netcat, you will see the output something like this:
192.168.0.10:50829 → 192.168.0.20:80 ip_size=44b tcp_size=0b proto=tcp ttl=64
writing(44) [0, 0, 0, 2, 69, 0, 0, 40, 0, 0, 64, 0, 64, 6, 185, 97, 192, 168, 0, 20, 192, 168, 0, 10, 0, 80, 198, 141, 0, 0, 0, 0, 83, 10, 20, 42, 80, 18, 255, 255, 0, 82, 0, 0]
192.168.0.10:50829 → 192.168.0.20:80 ip_size=20b tcp_size=0b proto=tcp ttl=64
If you close the netcat (Ctrl + C), then the packets start appearing!
192.168.0.10:50829 → 192.168.0.20:80 ip_size=20b tcp_size=0b proto=tcp ttl=64
192.168.0.10:50829 → 192.168.0.20:80 ip_size=20b tcp_size=0b proto=tcp ttl=64
192.168.0.10:50829 → 192.168.0.20:80 ip_size=20b tcp_size=0b proto=tcp ttl=64
192.168.0.10:50829 → 192.168.0.20:80 ip_size=20b tcp_size=0b proto=tcp ttl=64
How is it even receiving these! I also confirmed it running tshark:
$ tshark -i utun4
Capturing on 'utun4'
1 0.000000 192.168.0.10 → 192.168.0.20 TCP 68 50838 → 80 [SYN, ECN, CWR] Seq=0 Win=65535 Len=0 MSS=1460 WS=64 TSval=689213287 TSecr=0 SACK_PERM=1
2 0.000202 192.168.0.20 → 192.168.0.10 TCP 44 80 → 50838 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0
3 0.000245 192.168.0.10 → 192.168.0.20 TCP 44 50838 → 80 [ACK] Seq=1 Ack=1 Win=65535 Len=0
and when I close netcat:
$ tshark -i utun4
Capturing on 'utun4'
1 0.000000 192.168.0.10 → 192.168.0.20 TCP 68 50838 → 80 [SYN, ECN, CWR] Seq=0 Win=65535 Len=0 MSS=1460 WS=64 TSval=689213287 TSecr=0 SACK_PERM=1
2 0.000202 192.168.0.20 → 192.168.0.10 TCP 44 80 → 50838 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0
3 0.000245 192.168.0.10 → 192.168.0.20 TCP 44 50838 → 80 [ACK] Seq=1 Ack=1 Win=65535 Len=0
4 47.650802 192.168.0.10 → 192.168.0.20 TCP 44 50838 → 80 [FIN, ACK] Seq=1 Ack=1 Win=65535 Len=0
5 48.852183 192.168.0.10 → 192.168.0.20 TCP 44 [TCP Retransmission] 50838 → 80 [FIN, ACK] Seq=1 Ack=1 Win=65535 Len=0
6 51.053430 192.168.0.10 → 192.168.0.20 TCP 44 [TCP Retransmission] 50838 → 80 [FIN, ACK] Seq=1 Ack=1 Win=65535 Len=0
7 55.254724 192.168.0.10 → 192.168.0.20 TCP 44 [TCP Retransmission] 50838 → 80 [FIN, ACK] Seq=1 Ack=1 Win=65535 Len=0
8 63.458208 192.168.0.10 → 192.168.0.20 TCP 44 [TCP Retransmission] 50838 → 80 [FIN, ACK] Seq=1 Ack=1 Win=65535 Len=0
Where are the packets coming from! I am new to TUN/TAP, so it could be my understanding of these interfaces is not correct
So I can get readhalf and writehalf?
Following #19, I'm seeing that all of the data read by my tun device starts with the four bytes [0x00 0x00 0x00 0x02]
. I later see my payload in the byte stream but I'm wondering where the actual tun tap frame is?
From the source (https://www.kernel.org/doc/Documentation/networking/tuntap.txt) it states:
If flag IFF_NO_PI is not set each frame format is:
Flags [2 bytes]
Proto [2 bytes]
Raw protocol(IP, IPv6, etc) frame.
I am expecting an 0x0800
or 0x86dd
(ipv4 / ipv6) respectfully (see https://en.wikipedia.org/wiki/EtherType) but I don't see any of this in any of my traces.
Syscall read
and write
on tun's fd could be shared multithreaded. So it would be nice to have
impl<'a> Read for &'a Tun { ... }
impl<'a> Write for &'a Tun { ... }
I made some changes to this crate and it works on iOS: eycorsican@b5cdf61
The idea is that on iOS and Android, we can create and configure a TUN interface in native code, then pass the file descriptor
to rust-tun
to create an async interface for reading/writing.
I'm referring to this section of the readme:
It just werks, but you have to set up routing manually.
I'm not receiving any packets on my MacOs (similar to #53). Here's my skeleton setup:
// server
let mut config = tun::Configuration::default();
config
.address((192, 168, 55, 0))
.netmask((255, 255, 255, 0))
.up();
let mut dev = tun::create(&config).unwrap();
let _ = Command::new("route")
.args(["-n", "add", "-net", "192.168.55.0/24", "10.0.0.1"])
.output();
let mut buffer = vec![0; 1504]; // MTU + 4 for the header
loop {
let nbytes = dev.read(&mut buffer).unwrap();
// Output bytes (nothing gets read...)
}
// client
let socket = UdpSocket::bind("0.0.0.0:34254")?;
socket
.connect("192.168.55.2:31416")
.expect("connect function failed");
// sending non-empty buffer
socket.send(&buf)?;
I see this device in my ifconfig output:
utun4: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
inet 192.168.55.0 --> 10.0.0.255 netmask 0xffffff00
use nc command listen 9999 port,but cannot receive any data
nc -lk 10.2.0.2 9999
tun
is a great project used by many project. There are many issues and PRs are still waiting for discussion and reviews.
After contacted with @meh on E-mail, I found that he is getting sicker and sicker (pray for @meh ). Here I opened an issue to help for recruiting co-authors, who can help reviewing and even publishing codes to crates.io.
Are there any plans for multiqueue support?
Using this multiqueues it is possible to allocate separate file descriptors for the tunnel reader and the tunnel writer.
https://www.kernel.org/doc/Documentation/networking/tuntap.txt
https://lwn.net/Articles/522397/
This crate is very useful and convenient to use. However, the crate just lacks Windows platform support. I find there is a fork from this main branch that supports the Windows platform and basically mixed with this main branch very well, which has a consistent API. https://github.com/ssrlive/rust-tun, could this main repository pull this fork to the main branch and publish it on crates.io?
So device enabling doesn't work with the version 0.1.
There should be 0.1.1 with the device enabling.
I'm using the latest version 0.6.1
(asyn api) on Windows, quickly after I finished my code and tested it on Windows 11, I noticed that the memory consumption continued to increase(over 1MB increase in 1 second) and never got freed, so I used Visual Studio to detach to the process and take several memory snapshots to take a close look into this, please check the attached images, from my prospect of view, it's likely coming from wintun
crate or even windows
crate, can anybody look into this?
https://crates.io/crates/ioctl
Lines 23 to 24 in e762c41
The ioctl-sys package also seems to be abandoned.
use packet::{builder::Builder, icmp, ip, Packet};
use std::io::{Read, Write};
use std::net::Ipv4Addr;
fn main() {
let mut config = tun::Configuration::default();
config
.address((10, 0, 0, 1))
.netmask((255, 255, 255, 0))
.up();
#[cfg(target_os = "linux")]
config.platform(|config| {
config.packet_information(true);
});
let mut dev = tun::create(&config).unwrap();
let mut buf = [0; 4096];
loop {
println!("prepare to read");
let amount = dev.read(&mut buf).unwrap();
//println!("{:?}", &buf[0..amount]);
let read_buf = &buf[4..amount];
match ip::Packet::new(read_buf) {
Ok(ip::Packet::V4(pkt)) => match icmp::Packet::new(pkt.payload()) {
Ok(icmp) => match icmp.echo() {
Ok(icmp) => {
println!("packet comming!!!! src: {}, dest: {}",pkt.source(),pkt.destination());
let reply = ip::v4::Builder::default()
.id(0x42)
.unwrap()
.ttl(64)
.unwrap()
.source(pkt.destination())
.unwrap()
.destination(pkt.source())
.unwrap()
.icmp()
.unwrap()
.echo()
.unwrap()
.reply()
.unwrap()
.identifier(icmp.identifier())
.unwrap()
.sequence(icmp.sequence())
.unwrap()
.payload(icmp.payload())
.unwrap()
.build()
.unwrap();
let r = dev.write(&reply[..]).unwrap();
dev.flush().unwrap();
println!("write {r}");
}
_ => {}
},
_ => {}
},
Err(err) => println!("Received an invalid packet: {:?}", err),
_ => {}
}
}
}
This code is exactly the same as that of https://github.com/meh/rust-tun/blob/master/examples/ping-tun.rs except that the code uses synchronous version API. When running command ping 10.0.0.2
, the program can read packet, however, when trying to reply to the ICMP packet, dev.write
does not work. I'm not sure what wrong is here.
self?.packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32
in ios15 is not use
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.