Skip to content

Commit a4ef2b5

Browse files
committed
WIP: Replace pinger with surge-pinger crate
1 parent 3244631 commit a4ef2b5

File tree

4 files changed

+115
-68
lines changed

4 files changed

+115
-68
lines changed

Cargo.lock

+71
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

talpid-wireguard/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ chrono = { workspace = true, features = ["clock"] }
2828
tokio = { workspace = true, features = ["process", "rt-multi-thread", "fs"] }
2929
tunnel-obfuscation = { path = "../tunnel-obfuscation" }
3030
rand = "0.8.5"
31+
surge-ping = "0.8.0"
3132

3233
[target.'cfg(target_os="android")'.dependencies]
3334
duct = "0.13"

talpid-wireguard/src/lib.rs

+43-38
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use std::env;
1515

1616
use std::{
1717
convert::Infallible,
18-
io,
1918
net::IpAddr,
2019
path::Path,
2120
pin::Pin,
@@ -359,7 +358,7 @@ impl WireguardMonitor {
359358
gateway,
360359
#[cfg(any(target_os = "macos", target_os = "linux"))]
361360
iface_name_clone.clone(),
362-
dbg!(config.mtu),
361+
dbg!(config.mtu as usize),
363362
)
364363
.await
365364
.unwrap(); // TODO: detect real MTU
@@ -948,61 +947,67 @@ impl WireguardMonitor {
948947
async fn get_mtu(
949948
gateway: std::net::Ipv4Addr,
950949
#[cfg(any(target_os = "macos", target_os = "linux"))] iface_name: String,
951-
max_mtu: u16,
952-
) -> Result<u16> {
953-
let mut pinger = ping_monitor::imp::Pinger::new(
954-
gateway,
955-
#[cfg(any(target_os = "macos", target_os = "linux"))]
956-
iface_name,
957-
)
958-
.unwrap();
950+
max_mtu: usize,
951+
) -> Result<usize> {
952+
use surge_ping::{Client, Config, PingIdentifier, PingSequence};
953+
954+
let config_builder = Config::builder().kind(surge_ping::ICMP::V4);
955+
// let addr = std::net::SocketAddr::new(gateway.into(), 0);
956+
// let config_builder = config_builder.bind(addr);
957+
#[cfg(any(target_os = "macos", target_os = "linux"))]
958+
let config_builder = config_builder.interface(&iface_name);
959+
let config = config_builder.build();
960+
961+
let client = Client::new(&config).unwrap();
962+
let mut pinger = client
963+
.pinger(IpAddr::V4(gateway), PingIdentifier(111))
964+
.await;
965+
pinger.timeout(Duration::from_secs(5)); // TODO: choose a good timeout
959966

967+
// let mut buf = vec![0; max_mtu as usize];
960968
let step_size = 20;
961969
let min_mtu = 576; // TODO: Account for IPv6?
962970
let linspace = mtu_spacing(min_mtu, max_mtu, step_size);
963-
for mtu in &linspace {
964-
log::warn!("Sending {mtu}");
965-
ping_monitor::Pinger::send_icmp_sized(&mut pinger, *mtu).map_err(Error::SetMtu)?;
966-
}
971+
972+
const IPV4_HEADER_SIZE: usize = 20;
973+
const ICMP_HEADER_SIZE: usize = 8;
974+
967975
let mut largest_verified_mtu = min_mtu;
968-
// tokio::time::sleep(Duration::from_millis(3000)).await; // TODO: Remove this
969-
let mut buf = vec![0; max_mtu as usize];
970-
for _ in &linspace {
971-
let size = match pinger.receive_ping_response(&mut buf).await {
972-
Ok(size) => size,
973-
Err(ping_monitor::imp::Error::Read(e)) if e.kind() == io::ErrorKind::TimedOut => {
974-
// TODO: This can sometimes trigger with max_mtu == largest_verified_mtu,
975-
// investigate why
976-
log::warn!("Lowering MTU from {max_mtu} to {largest_verified_mtu} because of dropped packets");
977-
return Ok(largest_verified_mtu);
976+
for (i, mtu) in linspace.iter().enumerate() {
977+
log::warn!("Sending {mtu}");
978+
let buf = vec![0; *mtu - IPV4_HEADER_SIZE - ICMP_HEADER_SIZE]; // TODO: avoid allocating
979+
match pinger.ping(PingSequence(i as u16), &buf).await {
980+
Ok((packet, rtt)) => {
981+
println!("{:?} {:0.2?}", packet, rtt);
982+
let surge_ping::IcmpPacket::V4(packet) = packet else {
983+
panic!();
984+
};
985+
let size = packet.get_size() + IPV4_HEADER_SIZE;
986+
assert_eq!(size, *mtu);
987+
if size > largest_verified_mtu {
988+
largest_verified_mtu = size;
989+
}
990+
// TODO: Remove
991+
log::warn!("Got response of size {size}")
978992
}
979-
Err(e) => return Err(Error::SetMtu(e)),
993+
Err(e) => println!("{}", e),
980994
};
981-
debug_assert!(
982-
linspace.contains(&size.try_into().unwrap()),
983-
"Received PING response was not the size of any sent pings"
984-
);
985-
if size > largest_verified_mtu.into() {
986-
largest_verified_mtu = size as u16;
987-
}
988-
// TODO: Remove
989-
log::warn!("Got response of size {size}")
990995
}
991-
// TODO: Remove
996+
992997
log::debug!("MTU {largest_verified_mtu} verified");
993998

994999
Ok(largest_verified_mtu)
9951000
}
9961001

9971002
#[cfg(target_os = "linux")]
998-
fn mtu_spacing(x_start: u16, x_end: u16, step_size: u16) -> Vec<u16> {
1003+
fn mtu_spacing(x_start: usize, x_end: usize, step_size: usize) -> Vec<usize> {
9991004
if x_start > x_end {
10001005
log::warn!("Setting MTU to {x_end} which is lower than");
10011006
return vec![x_end];
10021007
// todo!("Handle manual MTU lower that minimum")
10031008
}
10041009
let in_between = ((x_start + 1)..x_end).filter(|x| x % step_size == 0);
1005-
let mut ret = Vec::with_capacity(((x_end - x_start) / 3 + 2) as usize);
1010+
let mut ret = Vec::with_capacity((x_end - x_start) / 3 + 2);
10061011
ret.push(x_start);
10071012
ret.extend(in_between);
10081013
ret.push(x_end);
@@ -1080,7 +1085,7 @@ pub enum TunnelError {
10801085
/// Failed to setup a tunnel device.
10811086
#[cfg(windows)]
10821087
#[error(display = "Failed to config IP interfaces on tunnel device")]
1083-
SetupIpInterfaces(#[error(source)] io::Error),
1088+
SetupIpInterfaces(#[error(source)] std::io::Error),
10841089

10851090
/// Failed to configure Wireguard sockets to bypass the tunnel.
10861091
#[cfg(target_os = "android")]

talpid-wireguard/src/ping_monitor/icmp.rs

-30
Original file line numberDiff line numberDiff line change
@@ -123,36 +123,6 @@ impl Pinger {
123123
result
124124
}
125125

126-
#[cfg(target_os = "linux")]
127-
pub async fn receive_ping_response(&mut self, buf: &mut [u8]) -> Result<usize> {
128-
// TODO: pick out sequence number
129-
// TODO: verify payload?
130-
// NOTE: This assumes abound peer address, which we do not for send
131-
132-
// tokio::net::TcpSocket::from(value)
133-
134-
use std::io::Read;
135-
let mut attempt = 0;
136-
loop {
137-
// NOTE: This read statement seems to always read one entire ping response in its
138-
// entirety. Is this guaranteed?
139-
match self.sock.read(buf) {
140-
Ok(size) => {
141-
return Ok(size);
142-
}
143-
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
144-
log::warn!("Retrying");
145-
attempt += 1;
146-
if attempt > 10 {
147-
return Err(Error::Read(io::ErrorKind::TimedOut.into()));
148-
}
149-
tokio::time::sleep(Duration::from_millis(100)).await;
150-
}
151-
Err(e) => return Err(Error::Read(e)),
152-
}
153-
}
154-
}
155-
156126
fn construct_icmpv4_packet(&mut self, buffer: &mut [u8]) -> Result<()> {
157127
if !construct_icmpv4_packet_inner(buffer, self) {
158128
return Err(Error::BufferTooSmall);

0 commit comments

Comments
 (0)