use crate::types::{Checksum, Flags, Port, RoundId, Sequence, TimeToLive, TraceId};
use crate::TypeOfService;
use std::net::IpAddr;
use std::time::SystemTime;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum ProbeStatus {
#[default]
NotSent,
Skipped,
Failed(ProbeFailed),
Awaited(Probe),
Complete(ProbeComplete),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Probe {
pub sequence: Sequence,
pub identifier: TraceId,
pub src_port: Port,
pub dest_port: Port,
pub ttl: TimeToLive,
pub round: RoundId,
pub sent: SystemTime,
pub flags: Flags,
}
impl Probe {
#[must_use]
#[allow(clippy::too_many_arguments)]
pub(crate) const fn new(
sequence: Sequence,
identifier: TraceId,
src_port: Port,
dest_port: Port,
ttl: TimeToLive,
round: RoundId,
sent: SystemTime,
flags: Flags,
) -> Self {
Self {
sequence,
identifier,
src_port,
dest_port,
ttl,
round,
sent,
flags,
}
}
#[allow(clippy::too_many_arguments)]
#[must_use]
pub(crate) const fn complete(
self,
host: IpAddr,
received: SystemTime,
icmp_packet_type: IcmpPacketType,
tos: Option<TypeOfService>,
expected_udp_checksum: Option<Checksum>,
actual_udp_checksum: Option<Checksum>,
extensions: Option<Extensions>,
) -> ProbeComplete {
ProbeComplete {
sequence: self.sequence,
identifier: self.identifier,
src_port: self.src_port,
dest_port: self.dest_port,
ttl: self.ttl,
round: self.round,
sent: self.sent,
host,
received,
icmp_packet_type,
tos,
expected_udp_checksum,
actual_udp_checksum,
extensions,
}
}
#[must_use]
pub(crate) const fn failed(self) -> ProbeFailed {
ProbeFailed {
sequence: self.sequence,
identifier: self.identifier,
src_port: self.src_port,
dest_port: self.dest_port,
ttl: self.ttl,
round: self.round,
sent: self.sent,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProbeComplete {
pub sequence: Sequence,
pub identifier: TraceId,
pub src_port: Port,
pub dest_port: Port,
pub ttl: TimeToLive,
pub round: RoundId,
pub sent: SystemTime,
pub host: IpAddr,
pub received: SystemTime,
pub icmp_packet_type: IcmpPacketType,
pub tos: Option<TypeOfService>,
pub expected_udp_checksum: Option<Checksum>,
pub actual_udp_checksum: Option<Checksum>,
pub extensions: Option<Extensions>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProbeFailed {
pub sequence: Sequence,
pub identifier: TraceId,
pub src_port: Port,
pub dest_port: Port,
pub ttl: TimeToLive,
pub round: RoundId,
pub sent: SystemTime,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IcmpPacketType {
TimeExceeded(IcmpPacketCode),
EchoReply(IcmpPacketCode),
Unreachable(IcmpPacketCode),
NotApplicable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IcmpPacketCode(pub u8);
#[derive(Debug, Clone)]
pub enum Response {
TimeExceeded(ResponseData, IcmpPacketCode, Option<Extensions>),
DestinationUnreachable(ResponseData, IcmpPacketCode, Option<Extensions>),
EchoReply(ResponseData, IcmpPacketCode),
TcpReply(ResponseData),
TcpRefused(ResponseData),
}
impl Response {
pub const fn data(&self) -> &ResponseData {
match self {
Self::TimeExceeded(data, _, _)
| Self::DestinationUnreachable(data, _, _)
| Self::EchoReply(data, _)
| Self::TcpReply(data)
| Self::TcpRefused(data) => data,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Extensions {
pub extensions: Vec<Extension>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Extension {
Unknown(UnknownExtension),
Mpls(MplsLabelStack),
}
impl Default for Extension {
fn default() -> Self {
Self::Unknown(UnknownExtension::default())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct MplsLabelStack {
pub members: Vec<MplsLabelStackMember>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct MplsLabelStackMember {
pub label: u32,
pub exp: u8,
pub bos: u8,
pub ttl: u8,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct UnknownExtension {
pub class_num: u8,
pub class_subtype: u8,
pub bytes: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct ResponseData {
pub recv: SystemTime,
pub addr: IpAddr,
pub proto_resp: ProtocolResponse,
}
impl ResponseData {
pub const fn new(recv: SystemTime, addr: IpAddr, proto_resp: ProtocolResponse) -> Self {
Self {
recv,
addr,
proto_resp,
}
}
}
#[derive(Debug, Clone)]
pub enum ProtocolResponse {
Icmp(IcmpProtocolResponse),
Udp(UdpProtocolResponse),
Tcp(TcpProtocolResponse),
}
#[derive(Debug, Clone)]
pub struct IcmpProtocolResponse {
pub identifier: u16,
pub sequence: u16,
pub tos: Option<TypeOfService>,
}
impl IcmpProtocolResponse {
pub const fn new(identifier: u16, sequence: u16, tos: Option<TypeOfService>) -> Self {
Self {
identifier,
sequence,
tos,
}
}
}
#[derive(Debug, Clone)]
pub struct UdpProtocolResponse {
pub identifier: u16,
pub dest_addr: IpAddr,
pub src_port: u16,
pub dest_port: u16,
pub tos: Option<TypeOfService>,
pub expected_udp_checksum: u16,
pub actual_udp_checksum: u16,
pub payload_len: u16,
pub has_magic: bool,
}
impl UdpProtocolResponse {
#[allow(clippy::too_many_arguments)]
pub const fn new(
identifier: u16,
dest_addr: IpAddr,
src_port: u16,
dest_port: u16,
tos: Option<TypeOfService>,
expected_udp_checksum: u16,
actual_udp_checksum: u16,
payload_len: u16,
has_magic: bool,
) -> Self {
Self {
identifier,
dest_addr,
src_port,
dest_port,
tos,
expected_udp_checksum,
actual_udp_checksum,
payload_len,
has_magic,
}
}
}
#[derive(Debug, Clone)]
pub struct TcpProtocolResponse {
pub dest_addr: IpAddr,
pub src_port: u16,
pub dest_port: u16,
pub tos: Option<TypeOfService>,
}
impl TcpProtocolResponse {
pub const fn new(
dest_addr: IpAddr,
src_port: u16,
dest_port: u16,
tos: Option<TypeOfService>,
) -> Self {
Self {
dest_addr,
src_port,
dest_port,
tos,
}
}
}
#[cfg(test)]
impl ProbeStatus {
#[must_use]
pub fn try_into_awaited(self) -> Option<Probe> {
if let Self::Awaited(awaited) = self {
Some(awaited)
} else {
None
}
}
#[must_use]
pub fn try_into_complete(self) -> Option<ProbeComplete> {
if let Self::Complete(complete) = self {
Some(complete)
} else {
None
}
}
}