refactor: migrate ICMP handling to icmp-socket crate
This commit is contained in:
5 files changed
+168
-210
No files matched your search
Generated
+11
-21
@@ -26,12 +26,6 @@ version = "1.0.102"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "arrayvec"
|
|
||||||
version = "0.7.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
@@ -221,9 +215,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.5.6"
|
version = "0.5.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4"
|
checksum = "2163a0e204a148662b6b6816d4b5d5668a5f2f8df498ccbd5cd0e864e78fecba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
]
|
]
|
||||||
@@ -294,15 +288,6 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "etherparse"
|
|
||||||
version = "0.19.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b119b9796ff800751a220394b8b3613f26dd30c48f254f6837e64c464872d1c7"
|
|
||||||
dependencies = [
|
|
||||||
"arrayvec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
@@ -389,6 +374,14 @@ version = "2.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
|
checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icmp-socket"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "git+https://github.com/Lochnair/icmp-socket.git?tag=v0.4.0#ca3695e3e104713fda9872546545dc5266f6ed4c"
|
||||||
|
dependencies = [
|
||||||
|
"socket2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "id-arena"
|
name = "id-arena"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
@@ -794,15 +787,12 @@ version = "0.2.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
"byteorder",
|
"icmp-socket",
|
||||||
"etherparse",
|
|
||||||
"log",
|
"log",
|
||||||
"neli",
|
"neli",
|
||||||
"rand",
|
"rand",
|
||||||
"rust-uci",
|
"rust-uci",
|
||||||
"rustix",
|
"rustix",
|
||||||
"serde",
|
|
||||||
"socket2",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|||||||
+4
-7
@@ -21,26 +21,23 @@ strip = "symbols"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
bincode = "2.0"
|
bincode = "2.0"
|
||||||
byteorder = "1.5"
|
icmp-socket = "0.4.0"
|
||||||
etherparse = "0.19"
|
|
||||||
neli = "0.7"
|
neli = "0.7"
|
||||||
rand = "0.10"
|
rand = "0.10"
|
||||||
rustix = { version = "1.1", features = ["fs", "thread", "time"] }
|
rustix = { version = "1.1", features = ["fs", "thread", "time"] }
|
||||||
rust-uci = { version = "0.1", optional = true }
|
rust-uci = { version = "0.1", optional = true }
|
||||||
socket2 = { version = "0.6", features = ["all"] }
|
|
||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
|
|
||||||
[dependencies.log]
|
[dependencies.log]
|
||||||
features = ["std"]
|
features = ["std"]
|
||||||
version = "0.4"
|
version = "0.4"
|
||||||
|
|
||||||
[dependencies.serde]
|
|
||||||
features = ["derive"]
|
|
||||||
version = "1.0"
|
|
||||||
|
|
||||||
[dependencies.time]
|
[dependencies.time]
|
||||||
features = ["formatting", "macros"]
|
features = ["formatting", "macros"]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
uci = ["dep:rust-uci"]
|
uci = ["dep:rust-uci"]
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
icmp-socket = { git = 'https://github.com/Lochnair/icmp-socket.git', tag = "v0.4.0"}
|
||||||
+17
-54
@@ -1,9 +1,8 @@
|
|||||||
use crate::MeasurementType;
|
use crate::MeasurementType;
|
||||||
use etherparse::err::packet::SliceError;
|
use icmp_socket::socket::IcmpSocket;
|
||||||
use log::debug;
|
use icmp_socket::{IcmpSocket4, Icmpv4Packet};
|
||||||
use socket2::{Domain, Protocol, SockAddr, Socket, Type};
|
use log::{debug};
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
@@ -15,13 +14,9 @@ pub enum PingError {
|
|||||||
#[error("Couldn't parse number")]
|
#[error("Couldn't parse number")]
|
||||||
InvalidNumber(#[from] io::Error),
|
InvalidNumber(#[from] io::Error),
|
||||||
#[error("Error parsing packet")]
|
#[error("Error parsing packet")]
|
||||||
InvalidPacket(#[from] SliceError),
|
InvalidPacket(String),
|
||||||
#[error("Invalid protocol")]
|
|
||||||
InvalidProtocol(String),
|
|
||||||
#[error("Invalid packet type")]
|
#[error("Invalid packet type")]
|
||||||
InvalidType(String),
|
InvalidType(String),
|
||||||
#[error("No transport")]
|
|
||||||
NoTransport,
|
|
||||||
#[error("Wrong ICMP identifier (expected {expected:?}, found {found:?})")]
|
#[error("Wrong ICMP identifier (expected {expected:?}, found {found:?})")]
|
||||||
WrongID { expected: u16, found: u16 },
|
WrongID { expected: u16, found: u16 },
|
||||||
}
|
}
|
||||||
@@ -39,34 +34,17 @@ pub struct PingReply {
|
|||||||
pub last_receive_time_s: Instant,
|
pub last_receive_time_s: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_socket(type_: MeasurementType) -> io::Result<Socket> {
|
fn open_socket(type_: MeasurementType) -> io::Result<IcmpSocket4> {
|
||||||
match type_ {
|
match type_ {
|
||||||
MeasurementType::Icmp | MeasurementType::IcmpTimestamps => {
|
MeasurementType::Icmp | MeasurementType::IcmpTimestamps => {
|
||||||
Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::ICMPV4))
|
IcmpSocket4::new()
|
||||||
}
|
},
|
||||||
MeasurementType::Ntp => Socket::new(Domain::IPV4, Type::DGRAM, None),
|
|
||||||
_ => {
|
_ => {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ReadFrom {
|
|
||||||
fn read_from(&mut self) -> io::Result<(Vec<u8>, SockAddr)>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ReadFrom for Socket {
|
|
||||||
fn read_from(&mut self) -> io::Result<(Vec<u8>, SockAddr)> {
|
|
||||||
let mut buffer = Vec::with_capacity(4096);
|
|
||||||
let (received, addr) = self.recv_from(buffer.spare_capacity_mut())?;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
buffer.set_len(received);
|
|
||||||
}
|
|
||||||
Ok((buffer, addr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait PingListener {
|
pub trait PingListener {
|
||||||
fn listen(
|
fn listen(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -78,15 +56,11 @@ pub trait PingListener {
|
|||||||
let socket = &mut open_socket(type_)?;
|
let socket = &mut open_socket(type_)?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (buf, sender) = match socket.read_from() {
|
let (pkt, sender) = match socket.rcv_from() {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(_) => continue,
|
Err(_) => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
// etherparse doesn't like when the size in the header doesn't match the buffer
|
|
||||||
// so resize the buffer when actual packet size is known
|
|
||||||
//buf = buf[..size].as_mut();
|
|
||||||
|
|
||||||
let addr: IpAddr = sender.as_socket().unwrap().ip();
|
let addr: IpAddr = sender.as_socket().unwrap().ip();
|
||||||
|
|
||||||
let reflectors = reflectors_lock.read().unwrap();
|
let reflectors = reflectors_lock.read().unwrap();
|
||||||
@@ -94,7 +68,7 @@ pub trait PingListener {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reply = match self.parse_packet(id, addr, buf.as_slice()) {
|
let reply = match self.parse_packet(id, addr, pkt) {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// parse_packet will throw an error if it's an unknown protocol etc.
|
// parse_packet will throw an error if it's an unknown protocol etc.
|
||||||
@@ -108,7 +82,7 @@ pub trait PingListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_packet(&self, id: u16, reflector: IpAddr, buf: &[u8]) -> Result<PingReply, PingError>;
|
fn parse_packet(&self, id: u16, reflector: IpAddr, pkt: Icmpv4Packet) -> Result<PingReply, PingError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PingSender {
|
pub trait PingSender {
|
||||||
@@ -118,7 +92,7 @@ pub trait PingSender {
|
|||||||
type_: MeasurementType,
|
type_: MeasurementType,
|
||||||
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let socket = &open_socket(type_)?;
|
let mut socket = open_socket(type_)?;
|
||||||
|
|
||||||
let mut seq: u16 = 0;
|
let mut seq: u16 = 0;
|
||||||
let tick_duration_ms: u16 = 500;
|
let tick_duration_ms: u16 = 500;
|
||||||
@@ -131,23 +105,12 @@ pub trait PingSender {
|
|||||||
Duration::from_millis((tick_duration_ms / reflectors.len() as u16) as u64);
|
Duration::from_millis((tick_duration_ms / reflectors.len() as u16) as u64);
|
||||||
|
|
||||||
for reflector in reflectors.iter() {
|
for reflector in reflectors.iter() {
|
||||||
let addr: SockAddr = match reflector.is_ipv4() {
|
let addr: Ipv4Addr = match reflector {
|
||||||
true => {
|
IpAddr::V4(ipv4) => *ipv4,
|
||||||
let ip4 = Ipv4Addr::from_str(&*reflector.to_string()).unwrap();
|
IpAddr::V6(_) => unimplemented!(),
|
||||||
let sock4 = SocketAddrV4::new(ip4, 0);
|
|
||||||
sock4.into()
|
|
||||||
}
|
|
||||||
false => {
|
|
||||||
let ip6 = Ipv6Addr::from_str(&*reflector.to_string()).unwrap();
|
|
||||||
let sock6 = SocketAddrV6::new(ip6, 0, 0, 0);
|
|
||||||
sock6.into()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let buf_v = self.craft_packet(id, seq);
|
socket.send_to(addr, self.craft_packet(id, seq))?;
|
||||||
let buf = buf_v.as_slice();
|
|
||||||
|
|
||||||
socket.send_to(buf, &addr)?;
|
|
||||||
thread::sleep(sleep_duration);
|
thread::sleep(sleep_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,5 +122,5 @@ pub trait PingSender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn craft_packet(&self, id: u16, seq: u16) -> Vec<u8>;
|
fn craft_packet(&self, id: u16, seq: u16) -> icmp_socket::packet::Icmpv4Packet;
|
||||||
}
|
}
|
||||||
+50
-61
@@ -3,9 +3,9 @@ use std::time::Instant;
|
|||||||
|
|
||||||
use crate::pinger::{PingError, PingListener, PingReply, PingSender};
|
use crate::pinger::{PingError, PingListener, PingReply, PingSender};
|
||||||
use crate::time::Time;
|
use crate::time::Time;
|
||||||
use byteorder::*;
|
use icmp_socket::Icmpv4Message;
|
||||||
use etherparse::TransportSlice::{Icmpv4, Icmpv6};
|
use icmp_socket::Icmpv4Packet;
|
||||||
use etherparse::{IcmpEchoHeader, Icmpv4Header, Icmpv4Type, SlicedPacket};
|
use icmp_socket::packet::WithEchoRequest;
|
||||||
use rustix::thread::ClockId;
|
use rustix::thread::ClockId;
|
||||||
|
|
||||||
pub struct PingerICMPEchoListener {}
|
pub struct PingerICMPEchoListener {}
|
||||||
@@ -14,73 +14,62 @@ pub struct PingerICMPEchoSender {}
|
|||||||
|
|
||||||
impl PingListener for PingerICMPEchoListener {
|
impl PingListener for PingerICMPEchoListener {
|
||||||
// Result: RTT, down time, up time
|
// Result: RTT, down time, up time
|
||||||
fn parse_packet(&self, id: u16, reflector: IpAddr, buf: &[u8]) -> Result<PingReply, PingError> {
|
fn parse_packet(&self, id: u16, reflector: IpAddr, pkt: Icmpv4Packet) -> Result<PingReply, PingError> {
|
||||||
match SlicedPacket::from_ip(buf) {
|
match pkt.typ {
|
||||||
Err(err) => Err(PingError::InvalidPacket(err)),
|
0 => {
|
||||||
Ok(value) => match value.transport {
|
if let Icmpv4Message::EchoReply {
|
||||||
Some(Icmpv4(icmp)) => match icmp.icmp_type() {
|
identifier,
|
||||||
Icmpv4Type::EchoReply(echo) => {
|
sequence,
|
||||||
if echo.id != id {
|
payload,
|
||||||
return Err(PingError::WrongID {
|
} = pkt.message
|
||||||
expected: id,
|
{
|
||||||
found: echo.id,
|
if identifier != id {
|
||||||
});
|
return Err(PingError::WrongID {
|
||||||
}
|
expected: id,
|
||||||
|
found: identifier,
|
||||||
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(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
type_ => Err(PingError::InvalidType(format!("{:?}", type_))),
|
|
||||||
},
|
let time_sent = match payload.as_slice().try_into() {
|
||||||
Some(Icmpv6(slice)) => Err(PingError::InvalidProtocol(format!("{:?}", slice))),
|
Ok(bytes) => u64::from_be_bytes(bytes) as i64,
|
||||||
Some(type_) => Err(PingError::InvalidProtocol(format!("{:?}", type_))),
|
Err(_) => {
|
||||||
None => Err(PingError::NoTransport),
|
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 {
|
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 clock = Time::new(ClockId::Monotonic);
|
||||||
let time_ms = clock.to_milliseconds();
|
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
|
Icmpv4Packet::with_echo_request(id, seq, payload).unwrap()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+86
-67
@@ -1,89 +1,108 @@
|
|||||||
use crate::pinger::{PingError, PingListener, PingReply, PingSender};
|
use crate::pinger::{PingError, PingListener, PingReply, PingSender};
|
||||||
use crate::time::Time;
|
use crate::time::Time;
|
||||||
use etherparse::icmpv4::TimestampMessage;
|
use icmp_socket::packet::IcmpPacketBuildError;
|
||||||
use etherparse::TransportSlice::{Icmpv4, Icmpv6};
|
use icmp_socket::Icmpv4Message;
|
||||||
use etherparse::{Icmpv4Header, Icmpv4Type, SlicedPacket};
|
use icmp_socket::Icmpv4Packet;
|
||||||
use rustix::thread::ClockId;
|
use rustix::thread::ClockId;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
/// Construct a packet for the TimestampRequest messages.
|
||||||
|
pub trait WithTimestampRequest {
|
||||||
|
type Packet;
|
||||||
|
|
||||||
|
fn with_timestamp_request(
|
||||||
|
identifier: u16,
|
||||||
|
sequence: u16,
|
||||||
|
originate: u32,
|
||||||
|
receive: u32,
|
||||||
|
transmit: u32,
|
||||||
|
) -> Result<Self::Packet, IcmpPacketBuildError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WithTimestampRequest for Icmpv4Packet {
|
||||||
|
type Packet = Icmpv4Packet;
|
||||||
|
|
||||||
|
fn with_timestamp_request(
|
||||||
|
identifier: u16,
|
||||||
|
sequence: u16,
|
||||||
|
originate: u32,
|
||||||
|
receive: u32,
|
||||||
|
transmit: u32,
|
||||||
|
) -> Result<Self::Packet, IcmpPacketBuildError> {
|
||||||
|
Ok(Self {
|
||||||
|
typ: 13,
|
||||||
|
code: 0,
|
||||||
|
checksum: 0,
|
||||||
|
message: Icmpv4Message::Timestamp {
|
||||||
|
identifier,
|
||||||
|
sequence,
|
||||||
|
originate,
|
||||||
|
receive,
|
||||||
|
transmit,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct PingerICMPTimestampListener {}
|
pub struct PingerICMPTimestampListener {}
|
||||||
|
|
||||||
pub struct PingerICMPTimestampSender {}
|
pub struct PingerICMPTimestampSender {}
|
||||||
|
|
||||||
impl PingListener for PingerICMPTimestampListener {
|
impl PingListener for PingerICMPTimestampListener {
|
||||||
// Result: RTT, down time, up time
|
// Result: RTT, down time, up time
|
||||||
fn parse_packet(&self, id: u16, reflector: IpAddr, buf: &[u8]) -> Result<PingReply, PingError> {
|
fn parse_packet(&self, id: u16, reflector: IpAddr, pkt: Icmpv4Packet) -> Result<PingReply, PingError> {
|
||||||
match SlicedPacket::from_ip(buf) {
|
match pkt.typ {
|
||||||
Err(err) => Err(PingError::InvalidPacket(err)),
|
// 14 = Timestamp reply
|
||||||
Ok(value) => match value.transport {
|
14 => {
|
||||||
Some(Icmpv4(icmp)) => match icmp.icmp_type() {
|
if let Icmpv4Message::TimestampReply {
|
||||||
Icmpv4Type::TimestampReply(reply) => {
|
identifier,
|
||||||
if reply.id != id {
|
sequence,
|
||||||
return Err(PingError::WrongID {
|
originate,
|
||||||
expected: id,
|
receive,
|
||||||
found: reply.id,
|
transmit,
|
||||||
});
|
} = pkt.message
|
||||||
}
|
{
|
||||||
|
if identifier != id {
|
||||||
let time_now = Time::new(ClockId::Realtime);
|
return Err(PingError::WrongID {
|
||||||
let time_since_midnight = time_now.get_time_since_midnight();
|
expected: id,
|
||||||
|
found: identifier,
|
||||||
let originate_timestamp = reply.originate_timestamp;
|
});
|
||||||
let receive_timestamp = reply.receive_timestamp;
|
|
||||||
let transmit_timestamp = reply.transmit_timestamp;
|
|
||||||
|
|
||||||
let rtt: i64 = time_since_midnight - originate_timestamp as i64;
|
|
||||||
let dl_time: i64 = time_since_midnight - transmit_timestamp as i64;
|
|
||||||
let ul_time: i64 = receive_timestamp as i64 - originate_timestamp as i64;
|
|
||||||
|
|
||||||
Ok(PingReply {
|
|
||||||
reflector,
|
|
||||||
seq: reply.seq,
|
|
||||||
rtt,
|
|
||||||
current_time: time_since_midnight,
|
|
||||||
down_time: dl_time as f64,
|
|
||||||
up_time: ul_time as f64,
|
|
||||||
originate_timestamp: originate_timestamp as i64,
|
|
||||||
receive_timestamp: receive_timestamp as i64,
|
|
||||||
transmit_timestamp: transmit_timestamp as i64,
|
|
||||||
last_receive_time_s: Instant::now(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
type_ => Err(PingError::InvalidType(format!("{:?}", type_))),
|
|
||||||
},
|
let time_now = Time::new(ClockId::Realtime);
|
||||||
Some(Icmpv6(slice)) => Err(PingError::InvalidType(format!("{:?}", slice))),
|
let time_since_midnight = time_now.get_time_since_midnight();
|
||||||
Some(type_) => Err(PingError::InvalidType(format!("{:?}", type_))),
|
|
||||||
None => Err(PingError::NoTransport),
|
let rtt: i64 = time_since_midnight - originate as i64;
|
||||||
|
let dl_time: i64 = time_since_midnight - transmit as i64;
|
||||||
|
let ul_time: i64 = receive as i64 - originate as i64;
|
||||||
|
|
||||||
|
Ok(PingReply {
|
||||||
|
reflector,
|
||||||
|
seq: sequence,
|
||||||
|
rtt,
|
||||||
|
current_time: time_since_midnight,
|
||||||
|
down_time: dl_time as f64,
|
||||||
|
up_time: ul_time as f64,
|
||||||
|
originate_timestamp: originate as i64,
|
||||||
|
receive_timestamp: receive as i64,
|
||||||
|
transmit_timestamp: transmit as i64,
|
||||||
|
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 PingerICMPTimestampSender {
|
impl PingSender for PingerICMPTimestampSender {
|
||||||
fn craft_packet(&self, id: u16, seq: u16) -> Vec<u8> {
|
fn craft_packet(&self, id: u16, seq: u16) -> Icmpv4Packet {
|
||||||
let time_since_midnight = Time::new(ClockId::Realtime).get_time_since_midnight();
|
let time_since_midnight = Time::new(ClockId::Realtime).get_time_since_midnight();
|
||||||
|
Icmpv4Packet::with_timestamp_request(id, seq, time_since_midnight as u32, 0, 0).unwrap()
|
||||||
let payload: [u8; 0] = [];
|
|
||||||
|
|
||||||
// Construct a header with checksum based on the payload
|
|
||||||
let hdr = Icmpv4Header::with_checksum(
|
|
||||||
Icmpv4Type::TimestampRequest(TimestampMessage {
|
|
||||||
id,
|
|
||||||
seq,
|
|
||||||
originate_timestamp: time_since_midnight as u32,
|
|
||||||
receive_timestamp: 0,
|
|
||||||
transmit_timestamp: 0,
|
|
||||||
}),
|
|
||||||
&payload,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create a buffer to hold the result of header + payload
|
|
||||||
let mut result = Vec::<u8>::with_capacity(hdr.header_len());
|
|
||||||
|
|
||||||
// Write the header to the buffer
|
|
||||||
hdr.write(&mut result).expect("Error writing packet");
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user