use bytes::Bytes;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use crate::extractor::L4Proto;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum IcmpFamily {
V4,
V6,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct IcmpMessage {
pub family: IcmpFamily,
pub ty: IcmpType,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(tag = "family", content = "ty", rename_all = "snake_case")
)]
#[non_exhaustive]
pub enum IcmpType {
V4(Icmpv4Type),
V6(Icmpv6Type),
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename_all = "snake_case"))]
#[non_exhaustive]
pub enum Icmpv4Type {
EchoReply { id: u16, seq: u16 },
DestinationUnreachable {
code: Icmpv4DestUnreachCode,
inner: Option<IcmpInner>,
},
Redirect {
code: Icmpv4RedirectCode,
gateway: Ipv4Addr,
inner: Option<IcmpInner>,
},
EchoRequest { id: u16, seq: u16 },
TimeExceeded {
code: Icmpv4TimeExceededCode,
inner: Option<IcmpInner>,
},
ParameterProblem {
pointer: u8,
inner: Option<IcmpInner>,
},
Timestamp {
id: u16,
seq: u16,
originate: u32,
receive: u32,
transmit: u32,
},
TimestampReply {
id: u16,
seq: u16,
originate: u32,
receive: u32,
transmit: u32,
},
Other {
raw_type: u8,
raw_code: u8,
raw_body: Bytes,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(tag = "kind", content = "value", rename_all = "snake_case")
)]
#[non_exhaustive]
pub enum Icmpv4DestUnreachCode {
Net,
Host,
Protocol,
Port,
FragmentationNeeded { mtu: Option<u16> },
SourceRouteFailed,
DestNetworkUnknown,
DestHostUnknown,
SourceHostIsolated,
NetworkProhibited,
HostProhibited,
NetworkTos,
HostTos,
CommunicationProhibited,
HostPrecedenceViolation,
PrecedenceCutoffInEffect,
Other(u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(tag = "kind", content = "value", rename_all = "snake_case")
)]
#[non_exhaustive]
pub enum Icmpv4RedirectCode {
Network,
Host,
Tos,
TosHost,
Other(u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(tag = "kind", content = "value", rename_all = "snake_case")
)]
#[non_exhaustive]
pub enum Icmpv4TimeExceededCode {
HopLimitExceeded,
FragmentReassemblyTimeExceeded,
Other(u8),
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename_all = "snake_case"))]
#[non_exhaustive]
pub enum Icmpv6Type {
DestinationUnreachable {
code: Icmpv6DestUnreachCode,
inner: Option<IcmpInner>,
},
PacketTooBig { mtu: u32, inner: Option<IcmpInner> },
TimeExceeded {
code: Icmpv6TimeExceededCode,
inner: Option<IcmpInner>,
},
ParameterProblem {
code: Icmpv6ParamProblemCode,
pointer: u32,
inner: Option<IcmpInner>,
},
EchoRequest { id: u16, seq: u16 },
EchoReply { id: u16, seq: u16 },
NeighborSolicitation { target: Ipv6Addr },
NeighborAdvertisement {
target: Ipv6Addr,
router: bool,
solicited: bool,
override_: bool,
},
Other {
raw_type: u8,
raw_code: u8,
raw_body: Bytes,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(tag = "kind", content = "value", rename_all = "snake_case")
)]
#[non_exhaustive]
pub enum Icmpv6DestUnreachCode {
NoRoute,
AdminProhibited,
BeyondScopeOfSource,
AddressUnreachable,
PortUnreachable,
SourceAddressFailedIngressPolicy,
RejectRouteToDestination,
Other(u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(tag = "kind", content = "value", rename_all = "snake_case")
)]
#[non_exhaustive]
pub enum Icmpv6TimeExceededCode {
HopLimitExceeded,
FragmentReassemblyTimeExceeded,
Other(u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(tag = "kind", content = "value", rename_all = "snake_case")
)]
#[non_exhaustive]
pub enum Icmpv6ParamProblemCode {
ErroneousHeaderField,
UnrecognizedNextHeaderType,
UnrecognizedIpv6Option,
Other(u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct IcmpInner {
pub src: IpAddr,
pub dst: IpAddr,
pub proto: L4Proto,
pub src_port: Option<u16>,
pub dst_port: Option<u16>,
}
impl IcmpType {
pub fn is_error(&self) -> bool {
matches!(
self,
IcmpType::V4(
Icmpv4Type::DestinationUnreachable { .. }
| Icmpv4Type::Redirect { .. }
| Icmpv4Type::TimeExceeded { .. }
| Icmpv4Type::ParameterProblem { .. },
) | IcmpType::V6(
Icmpv6Type::DestinationUnreachable { .. }
| Icmpv6Type::PacketTooBig { .. }
| Icmpv6Type::TimeExceeded { .. }
| Icmpv6Type::ParameterProblem { .. },
)
)
}
pub fn error_inner(&self) -> Option<(&'static str, &IcmpInner)> {
let (label, inner) = match self {
IcmpType::V4(Icmpv4Type::DestinationUnreachable { inner, .. }) => {
("dest_unreachable", inner.as_ref())
}
IcmpType::V4(Icmpv4Type::Redirect { inner, .. }) => ("redirect", inner.as_ref()),
IcmpType::V4(Icmpv4Type::TimeExceeded { inner, .. }) => {
("time_exceeded", inner.as_ref())
}
IcmpType::V4(Icmpv4Type::ParameterProblem { inner, .. }) => {
("parameter_problem", inner.as_ref())
}
IcmpType::V6(Icmpv6Type::DestinationUnreachable { inner, .. }) => {
("dest_unreachable", inner.as_ref())
}
IcmpType::V6(Icmpv6Type::PacketTooBig { inner, .. }) => {
("packet_too_big", inner.as_ref())
}
IcmpType::V6(Icmpv6Type::TimeExceeded { inner, .. }) => {
("time_exceeded", inner.as_ref())
}
IcmpType::V6(Icmpv6Type::ParameterProblem { inner, .. }) => {
("parameter_problem", inner.as_ref())
}
_ => return None,
};
inner.map(|i| (label, i))
}
pub fn short_kind(&self) -> &'static str {
match self {
IcmpType::V4(t) => match t {
Icmpv4Type::EchoReply { .. } => "echo_reply",
Icmpv4Type::DestinationUnreachable { .. } => "dest_unreachable",
Icmpv4Type::Redirect { .. } => "redirect",
Icmpv4Type::EchoRequest { .. } => "echo_request",
Icmpv4Type::TimeExceeded { .. } => "time_exceeded",
Icmpv4Type::ParameterProblem { .. } => "parameter_problem",
Icmpv4Type::Timestamp { .. } => "timestamp",
Icmpv4Type::TimestampReply { .. } => "timestamp_reply",
Icmpv4Type::Other { .. } => "other",
},
IcmpType::V6(t) => match t {
Icmpv6Type::DestinationUnreachable { .. } => "dest_unreachable",
Icmpv6Type::PacketTooBig { .. } => "packet_too_big",
Icmpv6Type::TimeExceeded { .. } => "time_exceeded",
Icmpv6Type::ParameterProblem { .. } => "parameter_problem",
Icmpv6Type::EchoRequest { .. } => "echo_request",
Icmpv6Type::EchoReply { .. } => "echo_reply",
Icmpv6Type::NeighborSolicitation { .. } => "neighbor_solicitation",
Icmpv6Type::NeighborAdvertisement { .. } => "neighbor_advertisement",
Icmpv6Type::Other { .. } => "other",
},
}
}
}
impl IcmpMessage {
pub fn is_error(&self) -> bool {
self.ty.is_error()
}
pub fn error_inner(&self) -> Option<(&'static str, &IcmpInner)> {
self.ty.error_inner()
}
pub fn short_kind(&self) -> &'static str {
self.ty.short_kind()
}
}