Skip to content

Commit

Permalink
feat: icmp extensions (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
fujiapple852 committed Oct 5, 2023
1 parent eb82c7e commit 88ae779
Show file tree
Hide file tree
Showing 13 changed files with 1,175 additions and 42 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ windows-sys = { version = "0.48.0", features = [
] }

[dev-dependencies]
hex-literal = "0.4.1"
rand = "0.8.5"
test-case = "3.2.1"

Expand Down
6 changes: 6 additions & 0 deletions src/tracing/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ pub const MAX_SEQUENCE_PER_ROUND: u16 = 1024;
/// require two rounds to ensure that delayed probe responses from the immediate prior round can be
/// detected and excluded.
pub const MAX_SEQUENCE: u16 = u16::MAX - (MAX_SEQUENCE_PER_ROUND * 2);

/// The maximum number of extensions allowed per probe response.
pub const MAX_EXTENSIONS_PER_PROBE: usize = 8;

/// The maximum number of members allowed per MPLS stack.
pub const MAX_MPLS_MEMBERS_PER_STACK: usize = 8;
3 changes: 3 additions & 0 deletions src/tracing/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ mod ipv4;
/// IPv6 implementation.
mod ipv6;

/// ICMP extensions.
mod extension;

/// Platform specific network code.
mod platform;

Expand Down
2 changes: 1 addition & 1 deletion src/tracing/net/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ impl Network for TracerChannel {
resp => Ok(resp),
},
}?;
if let Some(resp) = prob_response {
if let Some(resp) = &prob_response {
tracing::debug!(?resp);
}
Ok(prob_response)
Expand Down
61 changes: 61 additions & 0 deletions src/tracing/net/extension.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use crate::tracing::constants::{MAX_EXTENSIONS_PER_PROBE, MAX_MPLS_MEMBERS_PER_STACK};
use crate::tracing::error::TracerError;
use crate::tracing::packet::icmp_extension::extension_object::ClassNum;
use crate::tracing::packet::icmp_extension::extension_structure::ExtensionStructure;
use crate::tracing::packet::icmp_extension::mpls_label_stack::MplsLabelStack;
use crate::tracing::packet::icmp_extension::mpls_label_stack_member::MplsLabelStackMember;
use crate::tracing::probe::{
MplsExtensionData, MplsExtensionMember, ProbeResponseExtension, ProbeResponseExtensions,
};
use crate::tracing::util::Required;

impl TryFrom<&[u8]> for ProbeResponseExtensions {
type Error = TracerError;

fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Self::try_from(ExtensionStructure::new_view(value).req()?)
}
}

impl TryFrom<ExtensionStructure<'_>> for ProbeResponseExtensions {
type Error = TracerError;

fn try_from(value: ExtensionStructure<'_>) -> Result<Self, Self::Error> {
let extensions = value
.objects()
.take(MAX_EXTENSIONS_PER_PROBE)
.map(|obj| match obj.get_class_num() {
ClassNum::MultiProtocolLabelSwitchingLabelStack => {
MplsLabelStack::new_view(obj.payload())
.req()
.map(|mpls| ProbeResponseExtension::Mpls(MplsExtensionData::from(mpls)))
}
_ => Ok(ProbeResponseExtension::Unknown),
})
.collect::<Result<_, _>>()?;
Ok(Self { extensions })
}
}

impl From<MplsLabelStack<'_>> for MplsExtensionData {
fn from(value: MplsLabelStack<'_>) -> Self {
Self {
members: value
.members()
.take(MAX_MPLS_MEMBERS_PER_STACK)
.map(MplsExtensionMember::from)
.collect(),
}
}
}

impl From<MplsLabelStackMember<'_>> for MplsExtensionMember {
fn from(value: MplsLabelStackMember<'_>) -> Self {
Self {
label: value.get_label(),
exp: value.get_exp(),
bos: value.get_bos(),
ttl: value.get_ttl(),
}
}
}
35 changes: 23 additions & 12 deletions src/tracing/net/ipv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use crate::tracing::packet::tcp::TcpPacket;
use crate::tracing::packet::udp::UdpPacket;
use crate::tracing::packet::IpProtocol;
use crate::tracing::probe::{
ProbeResponse, ProbeResponseData, ProbeResponseSeq, ProbeResponseSeqIcmp, ProbeResponseSeqTcp,
ProbeResponseSeqUdp,
ProbeResponse, ProbeResponseData, ProbeResponseExtensions, ProbeResponseSeq,
ProbeResponseSeqIcmp, ProbeResponseSeqTcp, ProbeResponseSeqUdp,
};
use crate::tracing::types::{PacketSize, PayloadPattern, Sequence, TraceId, TypeOfService};
use crate::tracing::util::Required;
Expand Down Expand Up @@ -229,11 +229,10 @@ pub fn recv_tcp_socket(
}
if platform::is_host_unreachable_error(code) {
let error_addr = tcp_socket.icmp_error_info()?;
return Ok(Some(ProbeResponse::TimeExceeded(ProbeResponseData::new(
SystemTime::now(),
error_addr,
resp_seq,
))));
return Ok(Some(ProbeResponse::TimeExceeded(
ProbeResponseData::new(SystemTime::now(), error_addr, resp_seq),
None,
)));
}
}
}
Expand Down Expand Up @@ -332,16 +331,28 @@ fn extract_probe_resp(
Ok(match icmp_v4.get_icmp_type() {
IcmpType::TimeExceeded => {
let packet = TimeExceededPacket::new_view(icmp_v4.packet()).req()?;
let resp_seq = extract_probe_resp_seq(packet.payload(), protocol)?;
Some(ProbeResponse::TimeExceeded(ProbeResponseData::new(
recv, src, resp_seq,
)))
let payload = packet.payload();
let extension = packet
.extension()
.map(ProbeResponseExtensions::try_from)
.transpose()?;
let resp_seq = extract_probe_resp_seq(payload, protocol)?;
Some(ProbeResponse::TimeExceeded(
ProbeResponseData::new(recv, src, resp_seq),
extension,
))
}
IcmpType::DestinationUnreachable => {
let packet = DestinationUnreachablePacket::new_view(icmp_v4.packet()).req()?;
let resp_seq = extract_probe_resp_seq(packet.payload(), protocol)?;
let payload = packet.payload();
let extension = packet
.extension()
.map(ProbeResponseExtensions::try_from)
.transpose()?;
let resp_seq = extract_probe_resp_seq(payload, protocol)?;
Some(ProbeResponse::DestinationUnreachable(
ProbeResponseData::new(recv, src, resp_seq),
extension,
))
}
IcmpType::EchoReply => match protocol {
Expand Down
17 changes: 9 additions & 8 deletions src/tracing/net/ipv6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,10 @@ pub fn recv_tcp_socket(
}
if platform::is_host_unreachable_error(code) {
let error_addr = tcp_socket.icmp_error_info()?;
return Ok(Some(ProbeResponse::TimeExceeded(ProbeResponseData::new(
SystemTime::now(),
error_addr,
resp_seq,
))));
return Ok(Some(ProbeResponse::TimeExceeded(
ProbeResponseData::new(SystemTime::now(), error_addr, resp_seq),
None,
)));
}
}
}
Expand Down Expand Up @@ -263,15 +262,17 @@ fn extract_probe_resp(
IcmpType::TimeExceeded => {
let packet = TimeExceededPacket::new_view(icmp_v6.packet()).req()?;
let resp_seq = extract_probe_resp_seq(packet.payload(), protocol)?;
Some(ProbeResponse::TimeExceeded(ProbeResponseData::new(
recv, ip, resp_seq,
)))
Some(ProbeResponse::TimeExceeded(
ProbeResponseData::new(recv, ip, resp_seq),
None,
))
}
IcmpType::DestinationUnreachable => {
let packet = DestinationUnreachablePacket::new_view(icmp_v6.packet()).req()?;
let resp_seq = extract_probe_resp_seq(packet.payload(), protocol)?;
Some(ProbeResponse::DestinationUnreachable(
ProbeResponseData::new(recv, ip, resp_seq),
None,
))
}
IcmpType::EchoReply => match protocol {
Expand Down
6 changes: 5 additions & 1 deletion src/tracing/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ pub mod icmpv4;
/// `ICMPv6` packets.
pub mod icmpv6;

/// ICMP extensions
pub mod icmp_extension;

/// `IPv4` packets.
pub mod ipv4;

Expand All @@ -21,7 +24,8 @@ pub mod udp;
/// `TCP` packets.
pub mod tcp;

fn fmt_payload(bytes: &[u8]) -> String {
#[must_use]
pub fn fmt_payload(bytes: &[u8]) -> String {
use itertools::Itertools as _;
format!("{:02x}", bytes.iter().format(" "))
}
Expand Down
Loading

0 comments on commit 88ae779

Please sign in to comment.