Skip to content

Commit

Permalink
Merge pull request #14 from ssrlive/main
Browse files Browse the repository at this point in the history
Migrate to tun2
  • Loading branch information
SajjadPourali authored Jan 17, 2024
2 parents 4f509ca + 04b7ac7 commit 0c8177a
Show file tree
Hide file tree
Showing 10 changed files with 284 additions and 269 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Push or PR

on:
[push, pull_request]

env:
CARGO_TERM_COLOR: always

jobs:
build_n_test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v3
- name: rustfmt
run: cargo fmt --all -- --check
- name: check
run: cargo check --verbose
- name: clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Build
run: cargo build --verbose --examples --tests --all-features
- name: Test
run: cargo test --all-features --examples
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.vscode/
.VSCodeCounter/
/target/
Cargo.lock
.DS_Store
Expand Down
23 changes: 14 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ repository = 'https://github.com/narrowlink/ipstack'
# homepage = 'https://github.com/narrowlink/ipstack'
readme = "README.md"

[features]
default = []
log = ["tracing/log"]

[dependencies]
tokio = { version = "1.33", features = [
tokio = { version = "1.35", features = [
"sync",
"rt",
"time",
Expand All @@ -19,16 +23,17 @@ tokio = { version = "1.33", 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 }
tracing = { version = "0.1", default-features = false, features = [
"log",
], optional = true }

[dev-dependencies]
udp-stream = { version = "0.0.9", default-features = false }

[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dev-dependencies]
tun = { version = "0.6.1", features = ["async"], default-features = false }

[target.'cfg(target_os = "windows")'.dev-dependencies]
wintun = { version = "0.3", default-features = false }
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.
Expand Down
71 changes: 35 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,45 @@ Unstable, under development.
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use udp_stream::UdpStream;
use tokio::io{AsyncRead, AsyncWrite};
async fn copy_from_lhs_to_rhs(lhs:impl AsyncRead + AsyncWrite, rhs:impl AsyncRead + AsyncWrite){
let (lhs_reader,lhs_writer) = tokio::io::split(lhs);
let (rhs_reader, rhs_writer) = tokio::io::split(rhs);
tokio::join! {
tokio::io::copy(&mut lhs_reader, &mut rhs_writer) ,
tokio::io::copy(&mut rhs_reader, &mut lhs_writer),
}
async fn copy_from_lhs_to_rhs(lhs: impl AsyncRead + AsyncWrite, rhs: impl AsyncRead + AsyncWrite) {
let (mut lhs_reader, mut lhs_writer) = tokio::io::split(lhs);
let (mut rhs_reader, mut rhs_writer) = tokio::io::split(rhs);
let _r = tokio::join! {
tokio::io::copy(&mut lhs_reader, &mut rhs_writer) ,
tokio::io::copy(&mut rhs_reader, &mut lhs_writer),
};
}
#[tokio::main]
async fn main(){
const MTU: u16 = 1500;
let ipv4 = Ipv4Addr::new(10, 0, 0, 1);
let mut config = tun::Configuration::default();
config
.address(ipv4)
.netmask((255, 255, 255, 0))
.mtu(MTU as i32)
.up();
const MTU: u16 = 1500;
let ipv4 = Ipv4Addr::new(10, 0, 0, 1);
let netmask = Ipv4Addr::new(255, 255, 255, 0);
let mut config = tun::Configuration::default();
config.address(ipv4).netmask(netmask).mtu(MTU as i32).up();

let mut ipstack_config = ipstack::IpStackConfig::default();
ipstack_config.mtu(MTU);
ipstack_config.packet_information(cfg!(unix));
let mut ip_stack = ipstack::IpStack::new(ipstack_config, tun::create_as_async(&config).unwrap());

let mut ipstack_config = ipstack::IpStackConfig::default();
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).unwrap());
while let Ok(stream) = ip_stack.accept().await{
match stream{
IpStackStream::Tcp(tcp) => {
let rhs = TcpStream::connect("1.1.1.1:80").await.unwrap();
tokio::spawn(async move {
copy_from_lhs_to_rhs(tcp,rhs).await;
});
}
IpStackStream::Udp(udp) => {
let rhs = UdpStream::connect(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)), 53)).await.unwrap();
tokio::spawn(async move {
copy_from_lhs_to_rhs(udp,rhs).await;
});
}
}
}
while let Ok(stream) = ip_stack.accept().await {
match stream {
IpStackStream::Tcp(tcp) => {
let rhs = TcpStream::connect("1.1.1.1:80").await.unwrap();
tokio::spawn(async move {
copy_from_lhs_to_rhs(tcp, rhs).await;
});
}
IpStackStream::Udp(udp) => {
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)), 53);
let rhs = UdpStream::connect(addr).await.unwrap();
tokio::spawn(async move {
copy_from_lhs_to_rhs(udp, rhs).await;
});
}
}
}
}
````

We also suggest that you take a look at the complete [examples](https://github.com/narrowlink/ipstack/tree/main/examples).
We also suggest that you take a look at the complete [examples](examples).
76 changes: 76 additions & 0 deletions examples/echo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// 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<dyn Error>> {
let addr = env::args().nth(1).unwrap_or("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(())
}
112 changes: 75 additions & 37 deletions examples/tun.rs
Original file line number Diff line number Diff line change
@@ -1,73 +1,111 @@
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
//!
//! Build: `cargo build --examples`
//!
//! Usage:
//!
//! This example must be run as root or administrator privileges.
//! ```
//! sudo target/debug/examples/tun --server-addr 127.0.0.1:8080 # Linux or macOS
//! ```
//! Then please run the `echo` example server, which listens on TCP & UDP ports 127.0.0.1:8080.
//! ```
//! target/debug/examples/echo 127.0.0.1:8080
//! ```
//! To route traffic to the tun interface, run the following command with root or administrator privileges:
//! ```
//! 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 100 # Windows
//! sudo route add 1.2.3.4/32 10.0.0.1 # Apple macOS
//! ```
//! Now you can test it with `nc 1.2.3.4 any_port` or `nc -u 1.2.3.4 any_port`.
//! You can watch the echo information in the `nc` console.
//! ```
//! nc 1.2.3.4 2323 # TCP
//! nc -u 1.2.3.4 2323 # UDP
//! ```
//!
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);
#[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<dyn std::error::Error>> {
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();
config
.address(ipv4)
.netmask((255, 255, 255, 0))
.mtu(MTU as i32)
.up();
let mut config = tun2::Configuration::default();
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);
});

#[cfg(target_os = "windows")]
config.platform(|config| {
config.initialize(Some(12324323423423434234_u128));
});

let mut ipstack_config = ipstack::IpStackConfig::default();
ipstack_config.mtu(MTU);
ipstack_config.packet_info(cfg!(target_family = "unix"));
ipstack_config.packet_information(cfg!(unix));

let mut ip_stack =
ipstack::IpStack::new(ipstack_config, tun::create_as_async(&config).unwrap());
let mut ip_stack = ipstack::IpStack::new(ipstack_config, tun2::create_as_async(&config)?);

#[cfg(target_os = "macos")]
{
let s = format!("sudo route -n add -net 10.0.0.0/24 {}", ipv4);
let command = std::process::Command::new("sh")
.arg("-c")
.arg(s)
.output()
.unwrap();
if !command.status.success() {
panic!("cannot establish route to tun device");
}
};
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 ====");
});
}
};
Expand Down
Loading

0 comments on commit 0c8177a

Please sign in to comment.