-
Notifications
You must be signed in to change notification settings - Fork 184
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1f812fd
commit 6c19faa
Showing
4 changed files
with
192 additions
and
23 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,109 @@ | ||
//! Allows sending ICMP echo requests to a host in order to determine network latency. | ||
//! | ||
//! Based on /~https://github.com/tailscale/tailscale/blob/main/net/ping/ping.go. | ||
use std::{net::SocketAddr, sync::Arc, time::Duration}; | ||
use std::{fmt::Debug, net::IpAddr, sync::Arc, time::Duration}; | ||
|
||
use anyhow::Error; | ||
use anyhow::Result; | ||
use surge_ping::{Client, Config, IcmpPacket, PingIdentifier, PingSequence, ICMP}; | ||
use tracing::debug; | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct Pinger(Arc<Inner>); | ||
|
||
#[derive(Debug)] | ||
struct Inner {} | ||
impl Debug for Inner { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
f.debug_struct("Inner").finish() | ||
} | ||
} | ||
|
||
struct Inner { | ||
client_v6: Client, | ||
client_v4: Client, | ||
} | ||
|
||
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5); | ||
|
||
impl Pinger { | ||
pub async fn new() -> Result<Self, Error> { | ||
Ok(Self(Arc::new(Inner {}))) | ||
pub async fn new() -> Result<Self> { | ||
let client_v4 = Client::new(&Config::builder().kind(ICMP::V4).build())?; | ||
let client_v6 = Client::new(&Config::builder().kind(ICMP::V6).build())?; | ||
|
||
Ok(Self(Arc::new(Inner { | ||
client_v4, | ||
client_v6, | ||
}))) | ||
} | ||
|
||
pub async fn send(&self, addr: IpAddr, data: &[u8]) -> Result<Duration> { | ||
let client = match addr { | ||
IpAddr::V4(_) => &self.0.client_v4, | ||
IpAddr::V6(_) => &self.0.client_v6, | ||
}; | ||
let mut pinger = client.pinger(addr, PingIdentifier(rand::random())).await; | ||
pinger.timeout(DEFAULT_TIMEOUT); | ||
match pinger.ping(PingSequence(0), data).await? { | ||
(IcmpPacket::V4(packet), dur) => { | ||
debug!( | ||
"{} bytes from {}: icmp_seq={} ttl={:?} time={:0.2?}", | ||
packet.get_size(), | ||
packet.get_source(), | ||
packet.get_sequence(), | ||
packet.get_ttl(), | ||
dur | ||
); | ||
Ok(dur) | ||
} | ||
|
||
(IcmpPacket::V6(packet), dur) => { | ||
debug!( | ||
"{} bytes from {}: icmp_seq={} hlim={} time={:0.2?}", | ||
packet.get_size(), | ||
packet.get_source(), | ||
packet.get_sequence(), | ||
packet.get_max_hop_limit(), | ||
dur | ||
); | ||
Ok(dur) | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
use tracing_subscriber::{prelude::*, EnvFilter}; | ||
|
||
#[tokio::test] | ||
async fn test_ping_google() -> Result<()> { | ||
tracing_subscriber::registry() | ||
.with(tracing_subscriber::fmt::layer().with_writer(std::io::stderr)) | ||
.with(EnvFilter::from_default_env()) | ||
.try_init() | ||
.ok(); | ||
|
||
// Public DNS addrs from google based on | ||
// https://developers.google.com/speed/public-dns/docs/using | ||
|
||
let pinger = Pinger::new().await?; | ||
|
||
// IPv4 | ||
let dur = pinger.send("8.8.8.8".parse()?, &[1u8; 8]).await?; | ||
assert!(!dur.is_zero()); | ||
|
||
// IPv6 | ||
match pinger | ||
.send("2001:4860:4860:0:0:0:0:8888".parse()?, &[1u8; 8]) | ||
.await | ||
{ | ||
Ok(dur) => { | ||
assert!(!dur.is_zero()); | ||
} | ||
Err(err) => { | ||
tracing::error!("IPv6 is not available: {:?}", err); | ||
} | ||
} | ||
|
||
pub async fn send(&self, _addr: SocketAddr, _data: &[u8]) -> Result<Duration, Error> { | ||
anyhow::bail!("icmp is not available yet"); | ||
Ok(()) | ||
} | ||
} |