From 84dc81a23e49fdcc92544bfb8830518e00a3db7c Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Thu, 30 Nov 2023 09:42:34 +0800 Subject: [PATCH 01/11] reading code --- .gitignore | 2 + Cargo.toml | 11 ++- examples/echo.rs | 78 +++++++++++++++++++++ examples/tun.rs | 171 ++++++++++++++++------------------------------- src/lib.rs | 7 +- 5 files changed, 142 insertions(+), 127 deletions(-) create mode 100644 examples/echo.rs diff --git a/.gitignore b/.gitignore index f128f5e..0683989 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.vscode/ +.VSCodeCounter/ /target/ Cargo.lock .DS_Store diff --git a/Cargo.toml b/Cargo.toml index f8002ad..e07b91a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,25 +10,22 @@ repository = 'https://github.com/narrowlink/ipstack' readme = "README.md" [dependencies] -tokio = { version = "1.33", features = [ +tokio = { version = "1.34", features = [ "sync", "rt", "time", "io-util", "macros", + "rt-multi-thread", ], default-features = false } etherparse = { version = "0.13", default-features = false } thiserror = { version = "1.0", default-features = false } tracing = { version = "0.1.40", default-features = false } [dev-dependencies] +clap = { version = "4.4", features = ["derive"] } udp-stream = { version = "0.0.9", default-features = false } - -[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dev-dependencies] -tun = { version = "0.6", features = ["async"], default-features = false } - -[target.'cfg(target_os = "windows")'.dev-dependencies] -wintun = { version = "0.3", default-features = false } +tun = { git = "https://github.com/ssrlive/rust-tun.git", features = ["async"] } [profile.release] opt-level = 'z' # Optimize for size. diff --git a/examples/echo.rs b/examples/echo.rs new file mode 100644 index 0000000..e8aef9b --- /dev/null +++ b/examples/echo.rs @@ -0,0 +1,78 @@ +// +// tokio = { version = "1.33", features = ["full"] } +// +use std::{env, error::Error, io}; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::{TcpListener, UdpSocket}, +}; + +const TCP_TIMEOUT: u64 = 10 * 1000; // 10sec + +async fn tcp_main(addr: &str) -> io::Result<()> { + let listener = TcpListener::bind(addr).await?; + println!("[TCP] listening on: {}", addr); + loop { + let (mut socket, peer) = listener.accept().await?; + tokio::spawn(async move { + let block = async move { + let mut buf = vec![0; 1024]; + println!("[TCP] incoming peer {}", peer); + loop { + let duration = std::time::Duration::from_millis(TCP_TIMEOUT); + let n = tokio::time::timeout(duration, socket.read(&mut buf)).await??; + if n == 0 { + println!("[TCP] {} exit", peer); + break; + } + let amt = socket.write(&buf[0..n]).await?; + println!("[TCP] Echoed {}/{} bytes to {}", amt, n, peer); + } + Ok::<(), io::Error>(()) + }; + if let Err(err) = block.await { + println!("[TCP] {}", err); + } + }); + } +} + +async fn udp_main(addr: &str) -> io::Result<()> { + let socket = UdpSocket::bind(&addr).await?; + println!("[UDP] Listening on: {}", socket.local_addr()?); + + let mut buf = vec![0; 1024]; + let mut to_send = None; + + loop { + if let Some((size, peer)) = to_send { + let amt = socket.send_to(&buf[..size], &peer).await?; + println!("[UDP] Echoed {}/{} bytes to {}", amt, size, peer); + } + + to_send = Some(socket.recv_from(&mut buf).await?); + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let addr = env::args() + .nth(1) + .unwrap_or_else(|| "127.0.0.1:8080".to_string()); + + let addr1 = addr.clone(); + let tcp = tokio::spawn(async move { + tcp_main(&addr1).await?; + Ok::<(), io::Error>(()) + }); + + let udp = tokio::spawn(async move { + udp_main(&addr).await?; + Ok::<(), io::Error>(()) + }); + + tcp.await??; + udp.await??; + + Ok(()) +} diff --git a/examples/tun.rs b/examples/tun.rs index 572746e..6c9e6ca 100644 --- a/examples/tun.rs +++ b/examples/tun.rs @@ -1,147 +1,90 @@ -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - +/// +/// Build: `cargo build --examples` +/// Usage: `target/debug/examples/tun --server-addr 127.0.0.1:8080` +/// +/// This example must be run as root. +/// Then please run the `echo` example server, which listens on TCP & UDP ports 127.0.0.1:8080. +/// To route traffic to the tun interface, run the following command: +/// ``` +/// sudo ip route add 1.2.3.4/32 dev utun3 # linux +/// route add 1.2.3.4 mask 255.255.255.255 10.0.0.1 metric 6 # windows +/// ``` +/// Now you can test it with `nc 1.2.3.4 2323` or `nc -u 1.2.3.4 2323`. +/// You can watch the echo information in the `nc` console. +/// +use clap::Parser; use ipstack::stream::IpStackStream; +use std::net::{Ipv4Addr, SocketAddr}; use tokio::{join, net::TcpStream}; use udp_stream::UdpStream; // const MTU: u16 = 1500; const MTU: u16 = u16::MAX; -#[tokio::main(flavor = "current_thread")] -async fn main() { - let ipv4 = Ipv4Addr::new(10, 0, 0, 1); - #[cfg(not(target_os = "windows"))] +#[derive(Parser)] +#[command(author, version, about = "Testing app for tun.", long_about = None)] +struct Args { + /// echo server address, likes `127.0.0.1:8080` + #[arg(short, long, value_name = "IP:port")] + server_addr: SocketAddr, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let args = Args::parse(); + + let ipv4 = Ipv4Addr::new(10, 0, 0, 33); + let netmask = Ipv4Addr::new(255, 255, 255, 0); + let gateway = Ipv4Addr::new(10, 0, 0, 1); + let mut config = tun::Configuration::default(); - #[cfg(not(target_os = "windows"))] - config - .address(ipv4) - .netmask((255, 255, 255, 0)) - .mtu(MTU as i32) - .up(); + config.address(ipv4).netmask(netmask).mtu(MTU as i32).up(); + config.destination(gateway).name("utun3"); #[cfg(target_os = "linux")] config.platform(|config| { config.packet_information(true); }); + let mut ip_stack = ipstack::IpStack::new(tun::create_as_async(&config)?, MTU, true); - #[cfg(not(target_os = "windows"))] - let mut ip_stack = ipstack::IpStack::new(tun::create_as_async(&config).unwrap(), MTU, true); - - #[cfg(target_os = "windows")] - let mut ip_stack = ipstack::IpStack::new( - wintun::WinTunDevice::new(ipv4, Ipv4Addr::new(255, 255, 255, 0)), - MTU, - false, - ); + let server_addr = args.server_addr; loop { - match ip_stack.accept().await.unwrap() { + match ip_stack.accept().await? { IpStackStream::Tcp(tcp) => { - let s = TcpStream::connect("1.1.1.1:80").await.unwrap(); + let s = TcpStream::connect(server_addr).await; + if let Err(ref err) = s { + println!("connect TCP server failed \"{}\"", err); + continue; + } + println!("==== New TCP connection ===="); let (mut t_rx, mut t_tx) = tokio::io::split(tcp); - let (mut s_rx, mut s_tx) = tokio::io::split(s); + let (mut s_rx, mut s_tx) = tokio::io::split(s?); tokio::spawn(async move { - join! { + let _r = join! { tokio::io::copy(&mut t_rx, &mut s_tx) , tokio::io::copy(&mut s_rx, &mut t_tx), - } + }; + println!("====== end tcp connection ======"); }); } IpStackStream::Udp(udp) => { - let s = - UdpStream::connect(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)), 53)) - .await - .unwrap(); + let s = UdpStream::connect(server_addr).await; + if let Err(ref err) = s { + println!("connect UDP server failed \"{}\"", err); + continue; + } + println!("==== New UDP connection ===="); let (mut t_rx, mut t_tx) = tokio::io::split(udp); - let (mut s_rx, mut s_tx) = tokio::io::split(s); + let (mut s_rx, mut s_tx) = tokio::io::split(s?); tokio::spawn(async move { - join! { + let _r = join! { tokio::io::copy(&mut t_rx, &mut s_tx) , tokio::io::copy(&mut s_rx, &mut t_tx), - } + }; + println!("==== end UDP connection ===="); }); } }; } } - -#[cfg(target_os = "windows")] -mod wintun { - use std::{net::Ipv4Addr, sync::Arc, task::ready, thread}; - - use tokio::io::{AsyncRead, AsyncWrite}; - - pub struct WinTunDevice { - session: Arc, - receiver: tokio::sync::mpsc::UnboundedReceiver>, - _task: thread::JoinHandle<()>, - } - - impl WinTunDevice { - pub fn new(ip: Ipv4Addr, netmask: Ipv4Addr) -> WinTunDevice { - let wintun = unsafe { wintun::load() }.unwrap(); - let adapter = wintun::Adapter::create(&wintun, "IpStack", "Tunnel", None).unwrap(); - adapter.set_address(ip).unwrap(); - adapter.set_netmask(netmask).unwrap(); - let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap()); - let (receiver_tx, receiver_rx) = tokio::sync::mpsc::unbounded_channel::>(); - let session_reader = session.clone(); - let task = thread::spawn(move || { - loop { - let packet = session_reader.receive_blocking().unwrap(); - let bytes = packet.bytes().to_vec(); - // dbg!(&bytes); - receiver_tx.send(bytes).unwrap(); - } - }); - WinTunDevice { - session, - receiver: receiver_rx, - _task: task, - } - } - } - - impl AsyncRead for WinTunDevice { - fn poll_read( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut tokio::io::ReadBuf<'_>, - ) -> std::task::Poll> { - match ready!(self.receiver.poll_recv(cx)) { - Some(bytes) => { - buf.put_slice(&bytes); - std::task::Poll::Ready(Ok(())) - } - None => std::task::Poll::Ready(Ok(())), - } - } - } - - impl AsyncWrite for WinTunDevice { - fn poll_write( - self: std::pin::Pin<&mut Self>, - _cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - let mut write_pack = self.session.allocate_send_packet(buf.len() as u16)?; - write_pack.bytes_mut().copy_from_slice(buf.as_ref()); - self.session.send_packet(write_pack); - std::task::Poll::Ready(Ok(buf.len())) - } - - fn poll_flush( - self: std::pin::Pin<&mut Self>, - _cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - std::task::Poll::Ready(Ok(())) - } - - fn poll_shutdown( - self: std::pin::Pin<&mut Self>, - _cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - std::task::Poll::Ready(Ok(())) - } - } -} diff --git a/src/lib.rs b/src/lib.rs index e010567..2040428 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,16 +110,11 @@ impl IpStack { streams.remove(&packet.reverse_network_tuple()); continue; } - #[cfg(not(target_os = "windows"))] + #[allow(unused_mut)] let Ok(mut packet_byte) = packet.to_bytes() else{ trace!("to_bytes error"); continue; }; - #[cfg(target_os = "windows")] - let Ok(packet_byte) = packet.to_bytes() else{ - trace!("to_bytes error"); - continue; - }; #[cfg(not(target_os = "windows"))] if packet_info { if packet.src_addr().is_ipv4(){ From df0765ab7636f7e9d2846b779bf412e15a205faa Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:47:19 +0800 Subject: [PATCH 02/11] error handles --- Cargo.toml | 2 +- src/error.rs | 3 +++ src/lib.rs | 8 +++++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7aeb04e..0938caf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ tracing = { version = "0.1.40", default-features = false } [dev-dependencies] clap = { version = "4.4", features = ["derive"] } -udp-stream = { version = "0.0.9", default-features = false } +udp-stream = { version = "0.0", default-features = false } tun = { git = "https://github.com/ssrlive/rust-tun.git", features = ["async"] } [target.'cfg(target_os = "windows")'.dev-dependencies] diff --git a/src/error.rs b/src/error.rs index 93ca4cf..520e968 100644 --- a/src/error.rs +++ b/src/error.rs @@ -16,6 +16,9 @@ pub enum IpStackError { IoError(#[from] std::io::Error), #[error("Accept Error")] AcceptError, + + #[error("Send Error {0}")] + SendError(#[from] tokio::sync::mpsc::error::SendError), } impl From for std::io::Error { diff --git a/src/lib.rs b/src/lib.rs index fbb118d..0370f9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,7 +89,7 @@ impl IpStack { match IpStackTcpStream::new(packet.src_addr(),packet.dst_addr(),h, pkt_sender.clone(),mtu).await{ Ok(stream) => { entry.insert(stream.stream_sender()); - accept_sender.send(IpStackStream::Tcp(stream)).unwrap(); + accept_sender.send(IpStackStream::Tcp(stream))?; } Err(e) => { error!("{}",e); @@ -99,7 +99,7 @@ impl IpStack { IpStackPacketProtocol::Udp => { let stream = IpStackUdpStream::new(packet.src_addr(),packet.dst_addr(),packet.payload, pkt_sender.clone(),mtu); entry.insert(stream.stream_sender()); - accept_sender.send(IpStackStream::Udp(stream)).unwrap(); + accept_sender.send(IpStackStream::Udp(stream))?; } } } @@ -123,11 +123,13 @@ impl IpStack { packet_byte.splice(0..0, [TUN_FLAGS, TUN_PROTO_IP6].concat()); } } - device.write_all(&packet_byte).await.unwrap(); + device.write_all(&packet_byte).await?; // device.flush().await.unwrap(); } } } + #[allow(unreachable_code)] + Ok::<(), IpStackError>(()) }); IpStack { accept_receiver } From a8d622913a17cfb1753cd23c80481e21b261c3fe Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Thu, 14 Dec 2023 16:09:17 +0800 Subject: [PATCH 03/11] replace tracing crate to log crate --- Cargo.toml | 4 ++-- src/lib.rs | 9 ++++----- src/stream/tcp.rs | 15 +++++++-------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0938caf..a1579d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ repository = 'https://github.com/narrowlink/ipstack' readme = "README.md" [dependencies] -tokio = { version = "1.34", features = [ +tokio = { version = "1.35", features = [ "sync", "rt", "time", @@ -20,7 +20,7 @@ tokio = { version = "1.34", features = [ ], default-features = false } etherparse = { version = "0.13", default-features = false } thiserror = { version = "1.0", default-features = false } -tracing = { version = "0.1.40", default-features = false } +log = { version = "0.4", default-features = false } [dev-dependencies] clap = { version = "4.4", features = ["derive"] } diff --git a/src/lib.rs b/src/lib.rs index c1bdebd..0350f82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,6 @@ use tokio::{ select, sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, }; -use tracing::{error, trace}; use crate::{ packet::IpStackPacketProtocol, @@ -100,14 +99,14 @@ impl IpStack { let offset = if config.packet_info && cfg!(not(target_os = "windows")) {4} else {0}; // dbg!(&buffer[offset..n]); let Ok(packet) = NetworkPacket::parse(&buffer[offset..n])else{ - trace!("parse error"); + log::trace!("parse error"); continue; }; match streams.entry(packet.network_tuple()){ Occupied(entry) =>{ let t = packet.transport_protocol(); if let Err(_x) = entry.get().send(packet){ - trace!("{}", _x); + log::trace!("{}", _x); match t{ IpStackPacketProtocol::Tcp(_t) => { // dbg!(t.flags()); @@ -128,7 +127,7 @@ impl IpStack { accept_sender.send(IpStackStream::Tcp(stream))?; } Err(e) => { - error!("{}",e); + log::error!("{}",e); } } } @@ -148,7 +147,7 @@ impl IpStack { } #[allow(unused_mut)] let Ok(mut packet_byte) = packet.to_bytes() else{ - trace!("to_bytes error"); + log::trace!("to_bytes error"); continue; }; #[cfg(any(target_os = "macos", target_os = "linux"))] diff --git a/src/stream/tcp.rs b/src/stream/tcp.rs index 85c39cf..2c89eb7 100644 --- a/src/stream/tcp.rs +++ b/src/stream/tcp.rs @@ -21,7 +21,6 @@ use tokio::{ Notify, }, }; -use tracing::{trace, warn}; use crate::packet::NetworkPacket; @@ -191,7 +190,7 @@ impl AsyncRead for IpStackTcpStream { Pin::new(&mut self.tcb.timeout).poll(cx), std::task::Poll::Ready(_) ) { - trace!("timeout reached for {:?}", self.dst_addr); + log::trace!("timeout reached for {:?}", self.dst_addr); self.packet_sender .send(self.create_rev_packet( tcp_flags::RST | tcp_flags::ACK, @@ -470,13 +469,13 @@ impl AsyncWrite for IpStackTcpStream { .map_err(|_| Error::from(ErrorKind::UnexpectedEof))?; self.tcb.retransmission = None; } else if let Some(i) = self.tcb.retransmission { - warn!(i); - warn!(self.tcb.seq); - warn!(self.tcb.last_ack); - warn!(self.tcb.ack); + log::warn!("{}", i); + log::warn!("{}", self.tcb.seq); + log::warn!("{}", self.tcb.last_ack); + log::warn!("{}", self.tcb.ack); for p in self.tcb.inflight_packets.iter() { - warn!(p.seq); - warn!("{}", p.payload.len()); + log::warn!("{}", p.seq); + log::warn!("{}", p.payload.len()); } panic!("Please report these values at: https://github.com/narrowlink/ipstack/"); } From fe5e301674c5279c285c2f90b454ef25149bd7d9 Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:07:55 +0800 Subject: [PATCH 04/11] log feature --- Cargo.toml | 8 +++++++- src/lib.rs | 11 ++++++++--- src/stream/tcp.rs | 21 +++++++++++++-------- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 682382f..7b734c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,10 @@ repository = 'https://github.com/narrowlink/ipstack' # homepage = 'https://github.com/narrowlink/ipstack' readme = "README.md" +[features] +default = [] +log = ["tracing/log"] + [dependencies] tokio = { version = "1.35", features = [ "sync", @@ -20,7 +24,9 @@ tokio = { version = "1.35", features = [ ], default-features = false } etherparse = { version = "0.13", default-features = false } thiserror = { version = "1.0", default-features = false } -tracing = { version = "0.1", default-features = false, features = ["log"] } +tracing = { version = "0.1", default-features = false, features = [ + "log", +], optional = true } [dev-dependencies] clap = { version = "4.4", features = ["derive"] } diff --git a/src/lib.rs b/src/lib.rs index c1bdebd..5d026ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ use tokio::{ select, sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, }; +#[cfg(feature = "log")] use tracing::{error, trace}; use crate::{ @@ -99,7 +100,8 @@ impl IpStack { Ok(n) = device.read(&mut buffer) => { let offset = if config.packet_info && cfg!(not(target_os = "windows")) {4} else {0}; // dbg!(&buffer[offset..n]); - let Ok(packet) = NetworkPacket::parse(&buffer[offset..n])else{ + let Ok(packet) = NetworkPacket::parse(&buffer[offset..n]) else { + #[cfg(feature = "log")] trace!("parse error"); continue; }; @@ -107,6 +109,7 @@ impl IpStack { Occupied(entry) =>{ let t = packet.transport_protocol(); if let Err(_x) = entry.get().send(packet){ + #[cfg(feature = "log")] trace!("{}", _x); match t{ IpStackPacketProtocol::Tcp(_t) => { @@ -127,8 +130,9 @@ impl IpStack { entry.insert(stream.stream_sender()); accept_sender.send(IpStackStream::Tcp(stream))?; } - Err(e) => { - error!("{}",e); + Err(_e) => { + #[cfg(feature = "log")] + error!("{}", _e); } } } @@ -148,6 +152,7 @@ impl IpStack { } #[allow(unused_mut)] let Ok(mut packet_byte) = packet.to_bytes() else{ + #[cfg(feature = "log")] trace!("to_bytes error"); continue; }; diff --git a/src/stream/tcp.rs b/src/stream/tcp.rs index 85c39cf..c3d6fd0 100644 --- a/src/stream/tcp.rs +++ b/src/stream/tcp.rs @@ -21,6 +21,7 @@ use tokio::{ Notify, }, }; +#[cfg(feature = "log")] use tracing::{trace, warn}; use crate::packet::NetworkPacket; @@ -191,6 +192,7 @@ impl AsyncRead for IpStackTcpStream { Pin::new(&mut self.tcb.timeout).poll(cx), std::task::Poll::Ready(_) ) { + #[cfg(feature = "log")] trace!("timeout reached for {:?}", self.dst_addr); self.packet_sender .send(self.create_rev_packet( @@ -469,14 +471,17 @@ impl AsyncWrite for IpStackTcpStream { .send(packet) .map_err(|_| Error::from(ErrorKind::UnexpectedEof))?; self.tcb.retransmission = None; - } else if let Some(i) = self.tcb.retransmission { - warn!(i); - warn!(self.tcb.seq); - warn!(self.tcb.last_ack); - warn!(self.tcb.ack); - for p in self.tcb.inflight_packets.iter() { - warn!(p.seq); - warn!("{}", p.payload.len()); + } else if let Some(_i) = self.tcb.retransmission { + #[cfg(feature = "log")] + { + warn!(_i); + warn!(self.tcb.seq); + warn!(self.tcb.last_ack); + warn!(self.tcb.ack); + for p in self.tcb.inflight_packets.iter() { + warn!(p.seq); + warn!("{}", p.payload.len()); + } } panic!("Please report these values at: https://github.com/narrowlink/ipstack/"); } From 4350b2177404cbf0d833028b4ec2a5bd3bf6125e Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Tue, 16 Jan 2024 21:39:45 +0800 Subject: [PATCH 05/11] tun2 applied --- Cargo.toml | 7 +-- examples/tun.rs | 4 +- examples/wintun.rs | 151 --------------------------------------------- 3 files changed, 3 insertions(+), 159 deletions(-) delete mode 100644 examples/wintun.rs diff --git a/Cargo.toml b/Cargo.toml index 7b734c4..dc5168c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,12 +31,7 @@ tracing = { version = "0.1", default-features = false, features = [ [dev-dependencies] clap = { version = "4.4", features = ["derive"] } udp-stream = { version = "0.0", default-features = false } -tun = { git = "https://github.com/ssrlive/rust-tun.git", branch = "master", features = [ - "async", -] } - -[target.'cfg(target_os = "windows")'.dev-dependencies] -wintun = { version = "0.4", default-features = false } +tun2 = { version = "0.6", features = ["async"] } [profile.release] opt-level = 'z' # Optimize for size. diff --git a/examples/tun.rs b/examples/tun.rs index d66df2a..374d4cc 100644 --- a/examples/tun.rs +++ b/examples/tun.rs @@ -50,7 +50,7 @@ async fn main() -> Result<(), Box> { let netmask = Ipv4Addr::new(255, 255, 255, 0); let gateway = Ipv4Addr::new(10, 0, 0, 1); - let mut config = tun::Configuration::default(); + let mut config = tun2::Configuration::default(); config.address(ipv4).netmask(netmask).mtu(MTU as i32).up(); config.destination(gateway).name("utun3"); @@ -68,7 +68,7 @@ async fn main() -> Result<(), Box> { ipstack_config.mtu(MTU); ipstack_config.packet_info(cfg!(target_family = "unix")); - let mut ip_stack = ipstack::IpStack::new(ipstack_config, tun::create_as_async(&config)?); + let mut ip_stack = ipstack::IpStack::new(ipstack_config, tun2::create_as_async(&config)?); let server_addr = args.server_addr; diff --git a/examples/wintun.rs b/examples/wintun.rs deleted file mode 100644 index 34abb0f..0000000 --- a/examples/wintun.rs +++ /dev/null @@ -1,151 +0,0 @@ -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - -use ipstack::stream::IpStackStream; -use tokio::{join, net::TcpStream}; -use udp_stream::UdpStream; - -// const MTU: u16 = 1500; -const MTU: i32 = u16::MAX as i32; - -#[tokio::main(flavor = "current_thread")] -async fn main() { - let ipv4 = Ipv4Addr::new(10, 0, 0, 33); - let _netmask = Ipv4Addr::new(255, 255, 255, 0); - let _gateway = Ipv4Addr::new(10, 0, 0, 1); - #[cfg(not(target_os = "windows"))] - let mut config = tun::Configuration::default(); - #[cfg(not(target_os = "windows"))] - config.address(ipv4).netmask(_netmask).mtu(MTU).up(); - #[cfg(not(target_os = "windows"))] - config.destination(_gateway).name("utun3"); - - #[cfg(target_os = "linux")] - config.platform(|config| { - config.packet_information(true); - }); - - let mut ipstack_config = ipstack::IpStackConfig::default(); - ipstack_config.mtu(MTU as u16); - ipstack_config.packet_info(cfg!(target_family = "unix")); - - #[cfg(not(target_os = "windows"))] - let mut ip_stack = - ipstack::IpStack::new(ipstack_config, tun::create_as_async(&config).unwrap()); - - #[cfg(target_os = "windows")] - let mut ip_stack = ipstack::IpStack::new( - ipstack_config, - wintun::WinTunDevice::new(ipv4, Ipv4Addr::new(255, 255, 255, 0)), - ); - - loop { - match ip_stack.accept().await.unwrap() { - IpStackStream::Tcp(tcp) => { - let s = TcpStream::connect("1.1.1.1:80").await.unwrap(); - let (mut t_rx, mut t_tx) = tokio::io::split(tcp); - let (mut s_rx, mut s_tx) = tokio::io::split(s); - tokio::spawn(async move { - join! { - tokio::io::copy(&mut t_rx, &mut s_tx) , - tokio::io::copy(&mut s_rx, &mut t_tx), - } - }); - } - IpStackStream::Udp(udp) => { - let s = - UdpStream::connect(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)), 53)) - .await - .unwrap(); - let (mut t_rx, mut t_tx) = tokio::io::split(udp); - let (mut s_rx, mut s_tx) = tokio::io::split(s); - tokio::spawn(async move { - join! { - tokio::io::copy(&mut t_rx, &mut s_tx) , - tokio::io::copy(&mut s_rx, &mut t_tx), - } - }); - } - }; - } -} - -#[cfg(target_os = "windows")] -mod wintun { - use std::{net::Ipv4Addr, sync::Arc, task::ready, thread}; - - use tokio::io::{AsyncRead, AsyncWrite}; - - pub struct WinTunDevice { - session: Arc, - receiver: tokio::sync::mpsc::UnboundedReceiver>, - _task: thread::JoinHandle<()>, - } - - impl WinTunDevice { - pub fn new(ip: Ipv4Addr, netmask: Ipv4Addr) -> WinTunDevice { - let wintun = unsafe { wintun::load() }.unwrap(); - let adapter = wintun::Adapter::create(&wintun, "IpStack", "Tunnel", None).unwrap(); - adapter.set_address(ip).unwrap(); - adapter.set_netmask(netmask).unwrap(); - let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap()); - let (receiver_tx, receiver_rx) = tokio::sync::mpsc::unbounded_channel::>(); - let session_reader = session.clone(); - let task = thread::spawn(move || { - loop { - let packet = session_reader.receive_blocking().unwrap(); - let bytes = packet.bytes().to_vec(); - // dbg!(&bytes); - receiver_tx.send(bytes).unwrap(); - } - }); - WinTunDevice { - session, - receiver: receiver_rx, - _task: task, - } - } - } - - impl AsyncRead for WinTunDevice { - fn poll_read( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut tokio::io::ReadBuf<'_>, - ) -> std::task::Poll> { - match ready!(self.receiver.poll_recv(cx)) { - Some(bytes) => { - buf.put_slice(&bytes); - std::task::Poll::Ready(Ok(())) - } - None => std::task::Poll::Ready(Ok(())), - } - } - } - - impl AsyncWrite for WinTunDevice { - fn poll_write( - self: std::pin::Pin<&mut Self>, - _cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - let mut write_pack = self.session.allocate_send_packet(buf.len() as u16)?; - write_pack.bytes_mut().copy_from_slice(buf.as_ref()); - self.session.send_packet(write_pack); - std::task::Poll::Ready(Ok(buf.len())) - } - - fn poll_flush( - self: std::pin::Pin<&mut Self>, - _cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - std::task::Poll::Ready(Ok(())) - } - - fn poll_shutdown( - self: std::pin::Pin<&mut Self>, - _cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - std::task::Poll::Ready(Ok(())) - } - } -} From 5237e1ccbfd6ca401f973d39d77369e6112caec9 Mon Sep 17 00:00:00 2001 From: SajjadPourali Date: Tue, 16 Jan 2024 13:54:54 -0500 Subject: [PATCH 06/11] move tokio's rt-multi-thread feature to [dev-dependencies] --- Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index dc5168c..58e953a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ tokio = { version = "1.35", features = [ "time", "io-util", "macros", - "rt-multi-thread", ], default-features = false } etherparse = { version = "0.13", default-features = false } thiserror = { version = "1.0", default-features = false } @@ -32,6 +31,9 @@ tracing = { version = "0.1", default-features = false, features = [ clap = { version = "4.4", features = ["derive"] } udp-stream = { version = "0.0", default-features = false } tun2 = { version = "0.6", features = ["async"] } +tokio = { version = "1.35", features = [ + "rt-multi-thread", +], default-features = false } [profile.release] opt-level = 'z' # Optimize for size. From 0a2699e9482b39949392d1937960ddba024e7012 Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:42:12 +0800 Subject: [PATCH 07/11] refine code --- src/error.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/error.rs b/src/error.rs index 520e968..2b7f242 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,15 +1,12 @@ -use etherparse::WriteError; -use thiserror::Error; - #[allow(dead_code)] -#[derive(Error, Debug)] +#[derive(thiserror::Error, Debug)] pub enum IpStackError { #[error("The transport protocol is not supported")] UnsupportedTransportProtocol, #[error("The packet is invalid")] InvalidPacket, #[error("Write error: {0}")] - PacketWriteError(WriteError), + PacketWriteError(etherparse::WriteError), #[error("Invalid Tcp packet")] InvalidTcpPacket, #[error("IO error: {0}")] From 635e1bafbbe12060e15fe91eba3d52a5075b19fc Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Wed, 17 Jan 2024 11:05:41 +0800 Subject: [PATCH 08/11] support freebsd --- README.md | 2 +- examples/tun.rs | 2 +- src/lib.rs | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 403cb8a..56c9b67 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ async fn main(){ let mut ipstack_config = ipstack::IpStackConfig::default(); ipstack_config.mtu(MTU); - ipstack_config.packet_info(cfg!(target_family = "unix")); + ipstack_config.packet_info(cfg!(unix)); let mut ip_stack = ipstack::IpStack::new(ipstack_config, tun::create_as_async(&config).unwrap()); while let Ok(stream) = ip_stack.accept().await { diff --git a/examples/tun.rs b/examples/tun.rs index 374d4cc..ab0ef63 100644 --- a/examples/tun.rs +++ b/examples/tun.rs @@ -66,7 +66,7 @@ async fn main() -> Result<(), Box> { let mut ipstack_config = ipstack::IpStackConfig::default(); ipstack_config.mtu(MTU); - ipstack_config.packet_info(cfg!(target_family = "unix")); + ipstack_config.packet_info(cfg!(unix)); let mut ip_stack = ipstack::IpStack::new(ipstack_config, tun2::create_as_async(&config)?); diff --git a/src/lib.rs b/src/lib.rs index 5d026ea..dc841a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,18 +26,18 @@ pub mod stream; const DROP_TTL: u8 = 0; -#[cfg(not(target_os = "windows"))] +#[cfg(unix)] const TTL: u8 = 64; -#[cfg(target_os = "windows")] +#[cfg(windows)] const TTL: u8 = 128; -#[cfg(any(target_os = "linux", target_os = "macos"))] +#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))] const TUN_FLAGS: [u8; 2] = [0x00, 0x00]; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "freebsd"))] const TUN_PROTO_IP6: [u8; 2] = [0x86, 0xdd]; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "freebsd"))] const TUN_PROTO_IP4: [u8; 2] = [0x08, 0x00]; #[cfg(target_os = "macos")] @@ -98,7 +98,7 @@ impl IpStack { // dbg!(streams.len()); select! { Ok(n) = device.read(&mut buffer) => { - let offset = if config.packet_info && cfg!(not(target_os = "windows")) {4} else {0}; + let offset = if config.packet_info && cfg!(unix) {4} else {0}; // dbg!(&buffer[offset..n]); let Ok(packet) = NetworkPacket::parse(&buffer[offset..n]) else { #[cfg(feature = "log")] @@ -156,7 +156,7 @@ impl IpStack { trace!("to_bytes error"); continue; }; - #[cfg(any(target_os = "macos", target_os = "linux"))] + #[cfg(unix)] if config.packet_info { if packet.src_addr().is_ipv4(){ packet_byte.splice(0..0, [TUN_FLAGS, TUN_PROTO_IP4].concat()); From f91fead4c9d8f02e1f69aed859532c0a8cc85601 Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Wed, 17 Jan 2024 11:14:25 +0800 Subject: [PATCH 09/11] packet_information issues --- README.md | 2 +- examples/tun.rs | 2 +- src/lib.rs | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 56c9b67..fd9a620 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ async fn main(){ let mut ipstack_config = ipstack::IpStackConfig::default(); ipstack_config.mtu(MTU); - ipstack_config.packet_info(cfg!(unix)); + ipstack_config.packet_information(cfg!(unix)); let mut ip_stack = ipstack::IpStack::new(ipstack_config, tun::create_as_async(&config).unwrap()); while let Ok(stream) = ip_stack.accept().await { diff --git a/examples/tun.rs b/examples/tun.rs index ab0ef63..2af5e20 100644 --- a/examples/tun.rs +++ b/examples/tun.rs @@ -66,7 +66,7 @@ async fn main() -> Result<(), Box> { let mut ipstack_config = ipstack::IpStackConfig::default(); ipstack_config.mtu(MTU); - ipstack_config.packet_info(cfg!(unix)); + ipstack_config.packet_information(cfg!(unix)); let mut ip_stack = ipstack::IpStack::new(ipstack_config, tun2::create_as_async(&config)?); diff --git a/src/lib.rs b/src/lib.rs index dc841a2..8241bb4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ const TUN_PROTO_IP4: [u8; 2] = [0x00, 0x02]; pub struct IpStackConfig { pub mtu: u16, - pub packet_info: bool, + pub packet_information: bool, pub tcp_timeout: Duration, pub udp_timeout: Duration, } @@ -56,7 +56,7 @@ impl Default for IpStackConfig { fn default() -> Self { IpStackConfig { mtu: u16::MAX, - packet_info: false, + packet_information: false, tcp_timeout: Duration::from_secs(60), udp_timeout: Duration::from_secs(30), } @@ -73,8 +73,8 @@ impl IpStackConfig { pub fn mtu(&mut self, mtu: u16) { self.mtu = mtu; } - pub fn packet_info(&mut self, packet_info: bool) { - self.packet_info = packet_info; + pub fn packet_information(&mut self, packet_information: bool) { + self.packet_information = packet_information; } } @@ -98,7 +98,7 @@ impl IpStack { // dbg!(streams.len()); select! { Ok(n) = device.read(&mut buffer) => { - let offset = if config.packet_info && cfg!(unix) {4} else {0}; + let offset = if config.packet_information && cfg!(unix) {4} else {0}; // dbg!(&buffer[offset..n]); let Ok(packet) = NetworkPacket::parse(&buffer[offset..n]) else { #[cfg(feature = "log")] @@ -157,7 +157,7 @@ impl IpStack { continue; }; #[cfg(unix)] - if config.packet_info { + if config.packet_information { if packet.src_addr().is_ipv4(){ packet_byte.splice(0..0, [TUN_FLAGS, TUN_PROTO_IP4].concat()); } else{ From 5cf228e6af36222a213fe6c9a74d1605cfc1a16e Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Wed, 17 Jan 2024 13:14:21 +0800 Subject: [PATCH 10/11] minor changes --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 8241bb4..3856931 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,7 +32,7 @@ const TTL: u8 = 64; #[cfg(windows)] const TTL: u8 = 128; -#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))] +#[cfg(unix)] const TUN_FLAGS: [u8; 2] = [0x00, 0x00]; #[cfg(any(target_os = "linux", target_os = "freebsd"))] From 04b7ac7c8e1d9e533584c6f817fa24b63258299a Mon Sep 17 00:00:00 2001 From: SajjadPourali Date: Wed, 17 Jan 2024 00:16:41 -0500 Subject: [PATCH 11/11] let's omit freebsd for now --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3856931..6bfc485 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,9 +35,9 @@ const TTL: u8 = 128; #[cfg(unix)] const TUN_FLAGS: [u8; 2] = [0x00, 0x00]; -#[cfg(any(target_os = "linux", target_os = "freebsd"))] +#[cfg(target_os = "linux")] const TUN_PROTO_IP6: [u8; 2] = [0x86, 0xdd]; -#[cfg(any(target_os = "linux", target_os = "freebsd"))] +#[cfg(target_os = "linux")] const TUN_PROTO_IP4: [u8; 2] = [0x08, 0x00]; #[cfg(target_os = "macos")]