// SPDX-FileCopyrightText: 2022-Present Charles Corrigan mailto:chas-iot@runegate.org (github @chas-iot) // SPDX-FileCopyrightText: 2022-Present Daniel Lakeland mailto:dlakelan@street-artists.org (github @dlakelan) // SPDX-FileCopyrightText: 2022-Present Mark Baker mailto:mark@vpost.net (github @Fail-Safe) // SPDX-FileCopyrightText: 2022-Present Nils Andreas Svee mailto:contact@lochnair.net (github @Lochnair) // // SPDX-License-Identifier: MPL-2.0 use std::net::IpAddr; use std::time::Instant; use crate::pinger::{PingError, PingListener, PingReply, PingSender}; use crate::settings::MeasurementType; use crate::time::Time; use icmp_socket2::Icmpv4Message; use icmp_socket2::Icmpv4Packet; use icmp_socket2::packet::WithEchoRequest; use rustix::time::ClockId; pub struct PingerICMPEchoListener {} pub struct PingerICMPEchoSender {} impl PingListener for PingerICMPEchoListener { // Result: RTT, down time, up time fn parse_packet( &self, id: u16, reflector: IpAddr, measurement_type: MeasurementType, pkt: Icmpv4Packet, ) -> Result { match pkt.typ { 0 => { if let Icmpv4Message::EchoReply { identifier, sequence, payload, } = pkt.message { if identifier != id { return Err(PingError::WrongID { expected: id, found: identifier, }); } let time_sent = match payload.as_slice().try_into() { Ok(bytes) => u64::from_be_bytes(bytes) as i64, Err(_) => { return Err(PingError::InvalidPacket(format!( "Expected 8 bytes payload, but found {}", payload.len() ))); } }; let clock = Time::new(ClockId::Monotonic); let time_ms = clock.to_milliseconds() as i64; let rtt = (time_ms - time_sent) as f64; Ok(PingReply { reflector, measurement_type, seq: sequence, rtt, current_time: time_ms, down_time: (rtt / 2.0) as f64, up_time: (rtt / 2.0) as f64, originate_timestamp: time_sent, receive_timestamp: 0, transmit_timestamp: 0, last_receive_time_s: Instant::now(), }) } else { Err(PingError::InvalidPacket(format!( "Packet had type {:?}, but did not match the structure", pkt.typ ))) } } type_ => Err(PingError::InvalidType(format!("{:?}", type_))), } } } impl PingSender for PingerICMPEchoSender { fn craft_packet(&self, id: u16, seq: u16) -> (Icmpv4Packet, i64) { let clock = Time::new(ClockId::Monotonic); let time_ms = clock.to_milliseconds(); let payload = time_ms.to_be_bytes().to_vec(); ( Icmpv4Packet::with_echo_request(id, seq, payload).unwrap(), time_ms as i64, ) } }