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 6, 2023
1 parent c4ff03a commit ef69c54
Show file tree
Hide file tree
Showing 22 changed files with 1,448 additions and 88 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
11 changes: 10 additions & 1 deletion src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use std::sync::Arc;
use std::time::Duration;
use tracing::instrument;
use trippy::tracing::{
Probe, ProbeStatus, Tracer, TracerChannel, TracerChannelConfig, TracerConfig, TracerRound,
Probe, ProbeResponseExtensions, ProbeStatus, Tracer, TracerChannel, TracerChannelConfig,
TracerConfig, TracerRound,
};

/// The state of all hops in a trace.
Expand Down Expand Up @@ -114,6 +115,8 @@ impl Trace {
}
let host = probe.host.unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED));
*hop.addrs.entry(host).or_default() += 1;
// TODO should we combine extensions across rounds?
hop.extensions = probe.extensions.clone();
}
ProbeStatus::Awaited => {
let index = usize::from(probe.ttl.0) - 1;
Expand Down Expand Up @@ -164,6 +167,7 @@ pub struct Hop {
mean: f64,
m2: f64,
samples: Vec<Duration>,
extensions: Option<ProbeResponseExtensions>,
}

impl Hop {
Expand All @@ -186,6 +190,10 @@ impl Hop {
self.addrs.len()
}

pub fn extensions(&self) -> Option<&ProbeResponseExtensions> {
self.extensions.as_ref()
}

/// The total number of probes sent.
pub fn total_sent(&self) -> usize {
self.total_sent
Expand Down Expand Up @@ -259,6 +267,7 @@ impl Default for Hop {
mean: 0f64,
m2: 0f64,
samples: Vec::default(),
extensions: None,
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ pub struct TrippyConfig {
pub max_inflight: u8,
pub initial_sequence: u16,
pub tos: u8,
pub icmp_extensions: bool,
pub read_timeout: Duration,
pub packet_size: u16,
pub payload_pattern: u8,
Expand Down Expand Up @@ -340,6 +341,13 @@ impl TryFrom<(Args, u16)> for TrippyConfig {
cfg_file_strategy.tos,
constants::DEFAULT_STRATEGY_TOS,
);

let icmp_extensions = cfg_layer_bool_flag(
args.icmp_extensions,
cfg_file_strategy.icmp_extensions,
false,
);

let read_timeout = cfg_layer(
args.read_timeout,
cfg_file_strategy.read_timeout,
Expand Down Expand Up @@ -510,6 +518,7 @@ impl TryFrom<(Args, u16)> for TrippyConfig {
packet_size,
payload_pattern,
tos,
icmp_extensions,
source_addr,
interface,
port_direction,
Expand Down
4 changes: 4 additions & 0 deletions src/config/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ pub struct Args {
#[arg(short = 'Q', long)]
pub tos: Option<u8>,

/// Parse ICMP extensions
#[arg(short = 'e', long)]
pub icmp_extensions: bool,

/// The socket read timeout [default: 10ms]
#[arg(long)]
pub read_timeout: Option<String>,
Expand Down
1 change: 1 addition & 0 deletions src/config/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ pub struct ConfigStrategy {
pub packet_size: Option<u16>,
pub payload_pattern: Option<u8>,
pub tos: Option<u8>,
pub icmp_extensions: Option<bool>,
pub read_timeout: Option<String>,
}

Expand Down
24 changes: 23 additions & 1 deletion src/frontend/render/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use ratatui::widgets::{Block, BorderType, Borders, Cell, Row, Table};
use ratatui::Frame;
use std::net::IpAddr;
use std::rc::Rc;
use trippy::tracing::ProbeResponseExtension;

/// Render the table of data about the hops.
///
Expand Down Expand Up @@ -233,12 +234,33 @@ fn format_address(
let addr_fmt = match config.address_mode {
AddressMode::IP => addr.to_string(),
AddressMode::Host => {
if config.lookup_as_info {
let hostname = if config.lookup_as_info {
let entry = dns.lazy_reverse_lookup_with_asinfo(*addr);
format_dns_entry(entry, true, config.as_mode)
} else {
let entry = dns.lazy_reverse_lookup(*addr);
format_dns_entry(entry, false, config.as_mode)
};

// TODO just a hack for now...
if let Some(extensions) = hop.extensions() {
let mpls = extensions
.extensions
.iter()
.map(|ext| match ext {
ProbeResponseExtension::Unknown => todo!(),
ProbeResponseExtension::Mpls(mpls) => mpls
.members
.iter()
.map(|member| {
format!("[MPLS label: {}, ttl: {}]", member.label, member.ttl)
})
.join("\n"),
})
.join("\n");
format!("{hostname} {mpls}")
} else {
hostname
}
}
AddressMode::Both => {
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ fn make_channel_config(
args.payload_pattern,
args.multipath_strategy,
args.tos,
args.icmp_extensions,
args.read_timeout,
args.min_round_duration,
)
Expand Down
4 changes: 3 additions & 1 deletion src/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ pub use config::{
};
pub use net::channel::TracerChannel;
pub use net::source::SourceAddr;
pub use probe::{IcmpPacketType, Probe, ProbeStatus};
pub use probe::{
IcmpPacketType, Probe, ProbeResponseExtension, ProbeResponseExtensions, ProbeStatus,
};
pub use tracer::{Tracer, TracerRound};
3 changes: 3 additions & 0 deletions src/tracing/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ pub struct TracerChannelConfig {
pub payload_pattern: PayloadPattern,
pub multipath_strategy: MultipathStrategy,
pub tos: TypeOfService,
pub icmp_extensions: bool,
pub read_timeout: Duration,
pub tcp_connect_timeout: Duration,
}
Expand All @@ -173,6 +174,7 @@ impl TracerChannelConfig {
payload_pattern: u8,
multipath_strategy: MultipathStrategy,
tos: u8,
icmp_extensions: bool,
read_timeout: Duration,
tcp_connect_timeout: Duration,
) -> Self {
Expand All @@ -185,6 +187,7 @@ impl TracerChannelConfig {
payload_pattern: PayloadPattern(payload_pattern),
multipath_strategy,
tos: TypeOfService(tos),
icmp_extensions,
read_timeout,
tcp_connect_timeout,
}
Expand Down
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
20 changes: 15 additions & 5 deletions src/tracing/net/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub struct TracerChannel {
payload_pattern: PayloadPattern,
multipath_strategy: MultipathStrategy,
tos: TypeOfService,
icmp_extensions: bool,
read_timeout: Duration,
tcp_connect_timeout: Duration,
send_socket: Option<Socket>,
Expand Down Expand Up @@ -64,6 +65,7 @@ impl TracerChannel {
payload_pattern: config.payload_pattern,
multipath_strategy: config.multipath_strategy,
tos: config.tos,
icmp_extensions: config.icmp_extensions,
read_timeout: config.read_timeout,
tcp_connect_timeout: config.tcp_connect_timeout,
send_socket,
Expand Down Expand Up @@ -91,7 +93,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 Expand Up @@ -163,10 +165,10 @@ impl TracerChannel {
fn dispatch_tcp_probe(&mut self, probe: Probe) -> TraceResult<()> {
let socket = match (self.src_addr, self.dest_addr) {
(IpAddr::V4(src_addr), IpAddr::V4(dest_addr)) => {
ipv4::dispatch_tcp_probe(probe, src_addr, dest_addr, self.tos)
ipv4::dispatch_tcp_probe(&probe, src_addr, dest_addr, self.tos)
}
(IpAddr::V6(src_addr), IpAddr::V6(dest_addr)) => {
ipv6::dispatch_tcp_probe(probe, src_addr, dest_addr)
ipv6::dispatch_tcp_probe(&probe, src_addr, dest_addr)
}
_ => unreachable!(),
}?;
Expand All @@ -180,8 +182,16 @@ impl TracerChannel {
fn recv_icmp_probe(&mut self) -> TraceResult<Option<ProbeResponse>> {
if self.recv_socket.is_readable(self.read_timeout)? {
match self.dest_addr {
IpAddr::V4(_) => ipv4::recv_icmp_probe(&mut self.recv_socket, self.protocol),
IpAddr::V6(_) => ipv6::recv_icmp_probe(&mut self.recv_socket, self.protocol),
IpAddr::V4(_) => ipv4::recv_icmp_probe(
&mut self.recv_socket,
self.protocol,
self.icmp_extensions,
),
IpAddr::V6(_) => ipv6::recv_icmp_probe(
&mut self.recv_socket,
self.protocol,
self.icmp_extensions,
),
}
} else {
Ok(None)
Expand Down
60 changes: 60 additions & 0 deletions src/tracing/net/extension.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use crate::tracing::error::TracerError;
use crate::tracing::packet::icmp_extension::extension_object::{ClassNum, ExtensionObject};
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()
.flat_map(|obj| ExtensionObject::new_view(obj).req())
.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()
.flat_map(|member| MplsLabelStackMember::new_view(member).req())
.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(),
}
}
}
Loading

0 comments on commit ef69c54

Please sign in to comment.