reachability_core/
ping.rs1use anyhow::{anyhow, Context as _};
6use async_trait::async_trait;
7use fuchsia_async::{self as fasync, TimeoutExt as _};
8use futures::{FutureExt as _, SinkExt as _, TryFutureExt as _, TryStreamExt as _};
9use log::warn;
10use net_types::ip::{Ipv4, Ipv6};
11use std::net::SocketAddr;
12
13const PING_MESSAGE: &str = "Hello from reachability monitor!";
14const SEQ_MIN: u16 = 1;
15const SEQ_MAX: u16 = 3;
16const TIMEOUT: fasync::MonotonicDuration = fasync::MonotonicDuration::from_seconds(1);
17
18async fn ping<I>(interface_name: &str, addr: I::SockAddr) -> anyhow::Result<()>
19where
20 I: ping::FuchsiaIpExt,
21 I::SockAddr: std::fmt::Display + Copy,
22{
23 let socket = ping::new_icmp_socket::<I>().context("failed to create socket")?;
24 let () = socket
25 .bind_device(Some(interface_name.as_bytes()))
26 .with_context(|| format!("failed to bind socket to device {}", interface_name))?;
27 let (mut sink, mut stream) = ping::new_unicast_sink_and_stream::<
28 I,
29 _,
30 { PING_MESSAGE.len() + ping::ICMP_HEADER_LEN },
31 >(&socket, &addr, PING_MESSAGE.as_bytes());
32
33 for seq in SEQ_MIN..=SEQ_MAX {
34 let deadline = fasync::MonotonicInstant::after(TIMEOUT);
35 let () = sink
36 .send(seq)
37 .map_err(anyhow::Error::new)
38 .on_timeout(deadline, || Err(anyhow!("timed out")))
39 .await
40 .with_context(|| format!("failed to send ping (seq={})", seq))?;
41 if match stream.try_next().map(Some).on_timeout(deadline, || None).await {
42 None => Ok(false),
43 Some(Err(e)) => Err(anyhow!("failed to receive ping: {}", e)),
44 Some(Ok(None)) => Err(anyhow!("ping reply stream ended unexpectedly")),
45 Some(Ok(Some(got))) if got >= SEQ_MIN && got <= seq => Ok(true),
46 Some(Ok(Some(got))) => Err(anyhow!(
47 "received unexpected ping sequence number; got: {}, want: {}..={}",
48 got,
49 SEQ_MIN,
50 seq,
51 )),
52 }? {
53 return Ok(());
54 }
55 }
56 Err(anyhow!("no ping reply received"))
57}
58
59#[async_trait]
61pub trait Ping {
62 async fn ping(&self, interface_name: &str, addr: SocketAddr) -> bool;
64}
65
66pub struct Pinger;
67
68#[async_trait]
69impl Ping for Pinger {
70 async fn ping(&self, interface_name: &str, addr: SocketAddr) -> bool {
71 let r = match addr {
72 SocketAddr::V4(addr_v4) => ping::<Ipv4>(interface_name, addr_v4).await,
73 SocketAddr::V6(addr_v6) => ping::<Ipv6>(interface_name, addr_v6).await,
74 };
75 match r {
76 Ok(()) => true,
77 Err(e) => {
78 warn!("error while pinging {}: {:?}", addr, e);
79 false
80 }
81 }
82 }
83}