Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 69db09a

Browse files
committedMar 13, 2025
add netstack-system bench
1 parent 088b531 commit 69db09a

File tree

4 files changed

+303
-4
lines changed

4 files changed

+303
-4
lines changed
 

‎Cargo.lock

Lines changed: 56 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,17 @@ socket2-ext = { version = "0.1" }
3030

3131
netstack-smoltcp = { git = "https://github.com/automesh-network/netstack-smoltcp.git", rev = "62260478079d96b42fa524caa855609312c2cf43" }
3232
netstack-lwip = { git = "https://github.com/Watfaq/netstack-lwip.git", rev = "2817bf82740e04bbee6b7bf1165f55657a6ed163" }
33+
netstack-system = { git = "https://github.com/Watfaq/netstack-system.git", rev = "d8ffc602137cd826c6bf426432f07a1725c6d33f" }
3334

3435
[[bin]]
3536
name = "netstack-smoltcp"
3637
path = "src/netstack-smoltcp.rs"
3738

3839
[[bin]]
3940
name = "netstack-lwip"
40-
path = "src/netstack-lwip.rs"
41+
path = "src/netstack-lwip.rs"
42+
43+
[[bin]]
44+
name = "netstack-system"
45+
path = "src/netstack-system.rs"
46+

‎scripts/bench.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ install_iperf3
7373
print_blue "Building the binaries..."
7474
cargo build --bin=netstack-smoltcp --release
7575
cargo build --bin=netstack-lwip --release
76+
cargo build --bin=netstack-system --release
7677

7778
# 3. create network namespace
7879

@@ -129,6 +130,7 @@ run_benchmark() {
129130
print_blue "Running benchmarks..."
130131
run_benchmark "./target/release/netstack-smoltcp"
131132
run_benchmark "./target/release/netstack-lwip"
133+
run_benchmark "./target/release/netstack-system"
132134

133135
# 5. clean up
134136
kill $IPERF_SERVER_PID

‎src/netstack-system.rs

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
use std::net::{Ipv4Addr, SocketAddr};
2+
3+
use futures::{SinkExt, StreamExt};
4+
use netstack_system::UdpSocket;
5+
use structopt::StructOpt;
6+
use tokio::net::{TcpSocket, TcpStream};
7+
use tracing::{error, info, warn};
8+
use tun2::AbstractDevice;
9+
10+
#[derive(Debug, StructOpt)]
11+
#[structopt(name = "forward", about = "Simply forward tun tcp/udp traffic.")]
12+
struct Opt {
13+
/// Default binding interface, default by guessed.
14+
/// Specify but doesn't exist, no device is bound.
15+
#[structopt(short = "i", long = "interface")]
16+
interface: String,
17+
18+
/// name of the tun device, default to rtun8.
19+
#[structopt(short = "n", long = "name", default_value = "utun8")]
20+
name: String,
21+
22+
/// Tracing subscriber log level.
23+
#[structopt(long = "log-level", default_value = "debug")]
24+
log_level: tracing::Level,
25+
26+
/// Tokio current-thread runtime, default to multi-thread.
27+
#[structopt(long = "current-thread")]
28+
current_thread: bool,
29+
30+
/// Tokio task spawn_local, default to spwan.
31+
#[structopt(long = "local-task")]
32+
local_task: bool,
33+
}
34+
35+
fn main() {
36+
let opt = Opt::from_args();
37+
38+
let rt = if opt.current_thread {
39+
tokio::runtime::Builder::new_current_thread()
40+
} else {
41+
tokio::runtime::Builder::new_multi_thread()
42+
}
43+
.enable_all()
44+
.build()
45+
.unwrap();
46+
47+
rt.block_on(main_exec(opt));
48+
}
49+
50+
async fn main_exec(opt: Opt) {
51+
macro_rules! tokio_spawn {
52+
($fut: expr) => {
53+
if opt.local_task {
54+
tokio::task::spawn_local($fut)
55+
} else {
56+
tokio::task::spawn($fut)
57+
}
58+
};
59+
}
60+
61+
tracing::subscriber::set_global_default(
62+
tracing_subscriber::FmtSubscriber::builder()
63+
.with_max_level(opt.log_level)
64+
.finish(),
65+
)
66+
.unwrap();
67+
68+
let mut cfg = tun2::Configuration::default();
69+
cfg.layer(tun2::Layer::L3);
70+
let fd = -1;
71+
let addr = Ipv4Addr::new(10, 10, 10, 2);
72+
let gateway = Ipv4Addr::new(10, 10, 10, 1);
73+
if fd >= 0 {
74+
cfg.raw_fd(fd);
75+
} else {
76+
cfg.tun_name(&opt.name)
77+
.address(format!("{}", addr))
78+
.destination(format!("{}", gateway))
79+
.mtu(tun2::DEFAULT_MTU);
80+
#[cfg(not(any(target_arch = "mips", target_arch = "mips64",)))]
81+
{
82+
cfg.netmask("255.255.255.0");
83+
}
84+
cfg.up();
85+
}
86+
87+
let device = tun2::create_as_async(&cfg).unwrap();
88+
println!("created device: {:?}", device.address());
89+
90+
let framed = device.into_framed();
91+
let (tun_sink, mut tun_stream) = framed.split();
92+
93+
let mut futs = vec![];
94+
95+
let (stack, listener, udp_socket, stack_tx) = netstack_system::StackBuilder::new()
96+
.inet4_addr(addr)
97+
.build()
98+
.await;
99+
let listener = listener.unwrap();
100+
let udp_socket = udp_socket.unwrap();
101+
102+
futs.push(stack.process_loop(tun_sink));
103+
104+
futs.push(tokio_spawn!(async move {
105+
while let Some(pkt) = tun_stream.next().await {
106+
if let Ok(pkt) = pkt {
107+
let _ = stack_tx.send(pkt).await;
108+
}
109+
}
110+
}));
111+
112+
// Extracts TCP connections from stack and sends them to the dispatcher.
113+
futs.push(tokio_spawn!({
114+
let interface = opt.interface.clone();
115+
async move {
116+
handle_inbound_stream(listener, interface).await;
117+
}
118+
}));
119+
futs.push(tokio_spawn!({
120+
let interface = opt.interface.clone();
121+
async move {
122+
handle_inbound_datagram(udp_socket, interface).await;
123+
}
124+
}));
125+
futures::future::join_all(futs)
126+
.await
127+
.iter()
128+
.for_each(|res| {
129+
if let Err(e) = res {
130+
error!("error: {:?}", e);
131+
}
132+
});
133+
}
134+
135+
/// simply forward tcp stream
136+
async fn handle_inbound_stream(listener: netstack_system::TcpListener, interface: String) {
137+
while let Ok((mut stream, target)) = listener.accept().await {
138+
let interface: String = interface.clone();
139+
// let nat = nat.clone();
140+
tokio::spawn(async move {
141+
let remote = stream.peer_addr().unwrap();
142+
let local = stream.local_addr().unwrap();
143+
info!(
144+
"new tcp connection: {:?} => {:?}, actual remote: {}",
145+
remote, local, target
146+
);
147+
148+
match new_tcp_stream(target.into(), &interface).await {
149+
Ok(mut remote_stream) => {
150+
// pipe between two tcp stream
151+
match tokio::io::copy_bidirectional(&mut stream, &mut remote_stream).await {
152+
Ok(_) => {}
153+
Err(e) => warn!(
154+
"failed to copy tcp stream {:?}=>{:?}, err: {:?}",
155+
local, remote, e
156+
),
157+
}
158+
}
159+
Err(e) => warn!(
160+
"failed to new tcp stream {:?}=>{:?}, err: {:?}",
161+
local, remote, e
162+
),
163+
}
164+
});
165+
}
166+
}
167+
168+
/// simply forward udp datagram
169+
async fn handle_inbound_datagram(udp_socket: UdpSocket, interface: String) {
170+
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
171+
let (mut read_half, mut write_half) = udp_socket.split();
172+
tokio::spawn(async move {
173+
while let Some((data, local, remote)) = rx.recv().await {
174+
let _ = write_half.send((data, remote, local)).await;
175+
}
176+
});
177+
178+
while let Some((data, local, remote)) = read_half.next().await {
179+
let tx = tx.clone();
180+
let interface = interface.clone();
181+
tokio::spawn(async move {
182+
info!("new udp datagram: {:?} => {:?}", local, remote);
183+
match new_udp_packet(remote, &interface).await {
184+
Ok(remote_socket) => {
185+
// pipe between two udp sockets
186+
let _ = remote_socket.send(&data).await;
187+
loop {
188+
let mut buf = vec![0; 1024];
189+
match remote_socket.recv_from(&mut buf).await {
190+
Ok((len, _)) => {
191+
let _ = tx.send((buf[..len].to_vec(), local, remote));
192+
}
193+
Err(e) => {
194+
warn!(
195+
"failed to recv udp datagram {:?}<->{:?}: {:?}",
196+
local, remote, e
197+
);
198+
break;
199+
}
200+
}
201+
}
202+
}
203+
Err(e) => warn!(
204+
"failed to new udp socket {:?}=>{:?}, err: {:?}",
205+
local, remote, e
206+
),
207+
}
208+
});
209+
}
210+
}
211+
212+
async fn new_tcp_stream<'a>(addr: SocketAddr, iface: &str) -> std::io::Result<TcpStream> {
213+
use socket2_ext::{AddressBinding, BindDeviceOption};
214+
let socket = socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::STREAM, None)?;
215+
socket.bind_to_device(BindDeviceOption::v4(iface))?;
216+
socket.set_keepalive(true)?;
217+
socket.set_nodelay(true)?;
218+
socket.set_nonblocking(true)?;
219+
220+
let stream = TcpSocket::from_std_stream(socket.into())
221+
.connect(addr)
222+
.await?;
223+
224+
Ok(stream)
225+
}
226+
227+
async fn new_udp_packet(addr: SocketAddr, iface: &str) -> std::io::Result<tokio::net::UdpSocket> {
228+
use socket2_ext::{AddressBinding, BindDeviceOption};
229+
let socket = socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::DGRAM, None)?;
230+
socket.bind_to_device(BindDeviceOption::v4(iface))?;
231+
socket.set_nonblocking(true)?;
232+
233+
let socket = tokio::net::UdpSocket::from_std(socket.into());
234+
if let Ok(ref socket) = socket {
235+
socket.connect(addr).await?;
236+
}
237+
socket
238+
}

0 commit comments

Comments
 (0)
Please sign in to comment.