fix(icmp): handle RFC 792 timestamp rollover
This commit is contained in:
1 file changed
+48
-5
+48
-5
@@ -19,6 +19,19 @@ pub struct PingerICMPTimestampListener {}
|
|||||||
|
|
||||||
pub struct PingerICMPTimestampSender {}
|
pub struct PingerICMPTimestampSender {}
|
||||||
|
|
||||||
|
const MS_PER_DAY: i64 = 86_400_000;
|
||||||
|
|
||||||
|
fn timestamp_delta(later: i64, earlier: i64) -> i64 {
|
||||||
|
let delta = later - earlier;
|
||||||
|
if delta < -MS_PER_DAY / 2 {
|
||||||
|
delta + MS_PER_DAY
|
||||||
|
} else if delta > MS_PER_DAY / 2 {
|
||||||
|
delta - MS_PER_DAY
|
||||||
|
} else {
|
||||||
|
delta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PingListener for PingerICMPTimestampListener {
|
impl PingListener for PingerICMPTimestampListener {
|
||||||
// Result: RTT, down time, up time
|
// Result: RTT, down time, up time
|
||||||
fn parse_packet(
|
fn parse_packet(
|
||||||
@@ -46,12 +59,18 @@ impl PingListener for PingerICMPTimestampListener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let time_now = Time::new(ClockId::Realtime);
|
if receive & 0x8000_0000 != 0 || transmit & 0x8000_0000 != 0 {
|
||||||
let time_since_midnight = time_now.get_time_since_midnight();
|
return Err(PingError::InvalidPacket(
|
||||||
|
"non-standard RFC 792 timestamp".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let rtt = (time_since_midnight - originate as i64) as f64;
|
let time_since_midnight =
|
||||||
let dl_time = (time_since_midnight - transmit as i64) as f64;
|
Time::new(ClockId::Realtime).get_time_since_midnight();
|
||||||
let ul_time = (receive as i64 - originate as i64) as f64;
|
|
||||||
|
let rtt = timestamp_delta(time_since_midnight, originate as i64) as f64;
|
||||||
|
let dl_time = timestamp_delta(time_since_midnight, transmit as i64) as f64;
|
||||||
|
let ul_time = timestamp_delta(receive as i64, originate as i64) as f64;
|
||||||
|
|
||||||
Ok(PingReply {
|
Ok(PingReply {
|
||||||
reflector,
|
reflector,
|
||||||
@@ -88,3 +107,27 @@ impl PingSender for PingerICMPTimestampSender {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use icmp_socket2::packet::WithTimestampReply;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn timestamps_wrap_at_midnight_in_both_directions() {
|
||||||
|
assert_eq!(timestamp_delta(20, 86_399_900), 120);
|
||||||
|
assert_eq!(timestamp_delta(86_399_900, 20), -120);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_standard_rfc_792_timestamps_are_rejected() {
|
||||||
|
let packet = Icmpv4Packet::with_timestamp_reply(7, 1, 10, 0x8000_0001, 20).unwrap();
|
||||||
|
let result = PingerICMPTimestampListener {}.parse_packet(
|
||||||
|
7,
|
||||||
|
"192.0.2.1".parse().unwrap(),
|
||||||
|
MeasurementType::IcmpTimestamps,
|
||||||
|
packet,
|
||||||
|
);
|
||||||
|
assert!(matches!(result, Err(PingError::InvalidPacket(_))));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user