refactor: migrate ICMP handling to icmp-socket crate

This commit is contained in:
Nils Andreas Svee committed 2026-02-21 17:13:54 +07:00
1 parent 5f13511ee3
commit f5d3776159
5 files changed
+168 -210

No files matched your search

+50 -61
View File
@@ -3,9 +3,9 @@ use std::time::Instant;
use crate::pinger::{PingError, PingListener, PingReply, PingSender};
use crate::time::Time;
use byteorder::*;
use etherparse::TransportSlice::{Icmpv4, Icmpv6};
use etherparse::{IcmpEchoHeader, Icmpv4Header, Icmpv4Type, SlicedPacket};
use icmp_socket::Icmpv4Message;
use icmp_socket::Icmpv4Packet;
use icmp_socket::packet::WithEchoRequest;
use rustix::thread::ClockId;
pub struct PingerICMPEchoListener {}
@@ -14,73 +14,62 @@ pub struct PingerICMPEchoSender {}
impl PingListener for PingerICMPEchoListener {
// Result: RTT, down time, up time
fn parse_packet(&self, id: u16, reflector: IpAddr, buf: &[u8]) -> Result<PingReply, PingError> {
match SlicedPacket::from_ip(buf) {
Err(err) => Err(PingError::InvalidPacket(err)),
Ok(value) => match value.transport {
Some(Icmpv4(icmp)) => match icmp.icmp_type() {
Icmpv4Type::EchoReply(echo) => {
if echo.id != id {
return Err(PingError::WrongID {
expected: id,
found: echo.id,
});
}
let time_sent = icmp
.payload()
.read_u64::<NativeEndian>()
.expect("Couldn't parse payload to time")
as i64;
let clock = Time::new(ClockId::Monotonic);
let time_ms = clock.to_milliseconds() as i64;
let rtt: i64 = time_ms - time_sent;
Ok(PingReply {
reflector,
seq: echo.seq,
rtt,
current_time: time_ms,
down_time: (rtt / 2) as f64,
up_time: (rtt / 2) as f64,
originate_timestamp: 0,
receive_timestamp: 0,
transmit_timestamp: 0,
last_receive_time_s: Instant::now(),
})
fn parse_packet(&self, id: u16, reflector: IpAddr, pkt: Icmpv4Packet) -> Result<PingReply, PingError> {
match pkt.typ {
0 => {
if let Icmpv4Message::EchoReply {
identifier,
sequence,
payload,
} = pkt.message
{
if identifier != id {
return Err(PingError::WrongID {
expected: id,
found: identifier,
});
}
type_ => Err(PingError::InvalidType(format!("{:?}", type_))),
},
Some(Icmpv6(slice)) => Err(PingError::InvalidProtocol(format!("{:?}", slice))),
Some(type_) => Err(PingError::InvalidProtocol(format!("{:?}", type_))),
None => Err(PingError::NoTransport),
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: i64 = time_ms - time_sent;
Ok(PingReply {
reflector,
seq: sequence,
rtt,
current_time: time_ms,
down_time: (rtt / 2) as f64,
up_time: (rtt / 2) 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) -> Vec<u8> {
fn craft_packet(&self, id: u16, seq: u16) -> Icmpv4Packet {
let clock = Time::new(ClockId::Monotonic);
let time_ms = clock.to_milliseconds();
let payload = time_ms.to_ne_bytes();
let payload = time_ms.to_be_bytes().to_vec();
// Construct a header with checksum based on the payload
let hdr = Icmpv4Header::with_checksum(
Icmpv4Type::EchoRequest(IcmpEchoHeader { id, seq }),
&payload,
);
// Create a buffer to hold the result of header + payload
let mut result = Vec::<u8>::with_capacity(hdr.header_len() + payload.len());
// Write the header to the buffer
hdr.write(&mut result).expect("Error writing packet");
// Write the payload to the buffer
result.append(&mut payload.to_vec());
result
Icmpv4Packet::with_echo_request(id, seq, payload).unwrap()
}
}