From fd94840f4012da5adadf8baf5dc8f24d050a2929 Mon Sep 17 00:00:00 2001 From: FujiApple Date: Fri, 13 May 2022 18:05:57 +0800 Subject: [PATCH 1/6] refactor: rename `TracerChannel::new` as `TracerChannel:connect` --- src/main.rs | 2 +- src/tracing/net.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 02716650..bdaa795f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -163,7 +163,7 @@ fn start_backend( tracer_config: TracerConfig, trace_data: Arc>, ) -> anyhow::Result<()> { - let channel = TracerChannel::new(&tracer_config)?; + let channel = TracerChannel::connect(&tracer_config)?; thread::Builder::new() .name(format!("tracer-{}", tracer_config.trace_identifier.0)) .spawn(move || { diff --git a/src/tracing/net.rs b/src/tracing/net.rs index 34cc1830..e5fe6402 100644 --- a/src/tracing/net.rs +++ b/src/tracing/net.rs @@ -123,7 +123,7 @@ impl TracerChannel { /// Create an `IcmpChannel`. /// /// This operation requires the `CAP_NET_RAW` capability on Linux. - pub fn new(config: &TracerConfig) -> TraceResult { + pub fn connect(config: &TracerConfig) -> TraceResult { let src_addr = discover_ipv4_addr(config.target_addr, config.destination_port.0)?; let (icmp_tx, icmp_rx) = make_icmp_channel()?; let (udp_tx, _) = make_udp_channel()?; From adb547c32c0da9478998d1f0f506c27d84d57eb0 Mon Sep 17 00:00:00 2001 From: FujiApple Date: Fri, 13 May 2022 18:13:58 +0800 Subject: [PATCH 2/6] feat(net): added `src_addr` method to `TracerChannel` --- src/tracing/net.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tracing/net.rs b/src/tracing/net.rs index e5fe6402..8a00a24f 100644 --- a/src/tracing/net.rs +++ b/src/tracing/net.rs @@ -144,6 +144,12 @@ impl TracerChannel { tcp_probes: ArrayVec::new(), }) } + + /// Get the source `IpAddr` of the channel. + #[must_use] + pub fn src_addr(&self) -> IpAddr { + self.src_addr + } } impl Network for TracerChannel { From 172091f5e143b5669f82bd6acee368591c465124 Mon Sep 17 00:00:00 2001 From: FujiApple Date: Fri, 13 May 2022 18:20:34 +0800 Subject: [PATCH 3/6] feat: return `dest_addr` from `ProbeResponse::TcpRefused` and unify handling in `tracer` --- src/tracing/net.rs | 4 ++-- src/tracing/tracer.rs | 12 +----------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/tracing/net.rs b/src/tracing/net.rs index 8a00a24f..a09190bb 100644 --- a/src/tracing/net.rs +++ b/src/tracing/net.rs @@ -24,7 +24,7 @@ use pnet::transport::{ use pnet::util; use socket2::{Domain, Protocol, SockAddr, Socket, Type}; use std::io::ErrorKind; -use std::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr}; +use std::net::{IpAddr, Shutdown, SocketAddr}; use std::os::unix::io::AsRawFd; use std::time::{Duration, SystemTime}; @@ -289,7 +289,7 @@ impl TracerChannel { if nix::Error::from_i32(code) == nix::Error::ECONNREFUSED { return Ok(Some(ProbeResponse::TcpRefused(TcpProbeResponseData::new( SystemTime::now(), - IpAddr::V4(Ipv4Addr::UNSPECIFIED), + self.dest_addr, ttl, )))); } diff --git a/src/tracing/tracer.rs b/src/tracing/tracer.rs index 2a1a9db8..99a1e0a8 100644 --- a/src/tracing/tracer.rs +++ b/src/tracing/tracer.rs @@ -181,17 +181,7 @@ impl)> Tracer { st.complete_probe_echo_reply(sequence, host, received); } } - Some(ProbeResponse::TcpRefused(data)) => { - let ttl = TimeToLive(data.ttl); - let received = data.recv; - let host = self.target_addr; - let probe = st.probe_for_ttl(ttl).req()?; - let sequence = probe.sequence; - if st.in_round(sequence) { - st.complete_probe_other(sequence, host, received); - } - } - Some(ProbeResponse::TcpReply(data)) => { + Some(ProbeResponse::TcpReply(data) | ProbeResponse::TcpRefused(data)) => { let ttl = TimeToLive(data.ttl); let received = data.recv; let host = data.addr; From 90296ab67850ed77a87a0889da41405c8e14f857 Mon Sep 17 00:00:00 2001 From: FujiApple Date: Mon, 16 May 2022 12:39:02 +0800 Subject: [PATCH 4/6] refactor: create `TrippyConfig`, rename `TuiTraceInfo` as `TraceInfo`, refactor `main.rs` --- src/config.rs | 298 ++++++++++++++++++++++++++++++++++-------------- src/frontend.rs | 55 +-------- src/main.rs | 265 ++++++++++++++++++++++++------------------ 3 files changed, 371 insertions(+), 247 deletions(-) diff --git a/src/config.rs b/src/config.rs index 1f35ac6e..83578757 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,7 @@ +use anyhow::anyhow; use clap::{ArgEnum, Parser}; -use std::process::exit; use std::time::Duration; +use trippy::tracing::TracerProtocol; /// The maximum number of hops we allow. /// @@ -184,128 +185,251 @@ pub struct Args { pub report_cycles: usize, } +/// Fully parsed and validate configuration. +pub struct TrippyConfig { + pub targets: Vec, + pub protocol: TracerProtocol, + pub first_ttl: u8, + pub max_ttl: u8, + pub min_round_duration: Duration, + pub max_round_duration: Duration, + pub grace_duration: Duration, + pub max_inflight: u8, + pub initial_sequence: u16, + pub read_timeout: Duration, + pub packet_size: u16, + pub payload_pattern: u8, + pub source_port: u16, + pub destination_port: u16, + pub dns_timeout: Duration, + pub dns_resolve_method: DnsResolveMethod, + pub dns_lookup_as_info: bool, + pub tui_max_samples: usize, + pub tui_preserve_screen: bool, + pub tui_refresh_rate: Duration, + pub tui_address_mode: AddressMode, + pub max_addrs: Option, + pub mode: Mode, + pub report_cycles: usize, + pub max_rounds: Option, +} + +impl TryFrom<(Args, u16)> for TrippyConfig { + type Error = anyhow::Error; + + fn try_from(data: (Args, u16)) -> Result { + let (args, pid) = data; + let protocol = match args.protocol { + TraceProtocol::Icmp => TracerProtocol::Icmp, + TraceProtocol::Udp => TracerProtocol::Udp, + TraceProtocol::Tcp => TracerProtocol::Tcp, + }; + let read_timeout = humantime::parse_duration(&args.read_timeout)?; + let min_round_duration = humantime::parse_duration(&args.min_round_duration)?; + let max_round_duration = humantime::parse_duration(&args.max_round_duration)?; + let grace_duration = humantime::parse_duration(&args.grace_duration)?; + let source_port = args.source_port.unwrap_or_else(|| pid.max(1024)); + let tui_refresh_rate = humantime::parse_duration(&args.tui_refresh_rate)?; + let dns_timeout = humantime::parse_duration(&args.dns_timeout)?; + let max_rounds = match args.mode { + Mode::Stream | Mode::Tui => None, + Mode::Pretty | Mode::Markdown | Mode::Csv | Mode::Json => Some(args.report_cycles), + }; + validate_multi(args.mode, args.protocol, &args.targets)?; + validate_ttl(args.first_ttl, args.max_ttl)?; + validate_max_inflight(args.max_inflight)?; + validate_read_timeout(read_timeout)?; + validate_round_duration(min_round_duration, max_round_duration)?; + validate_grace_duration(grace_duration)?; + validate_packet_size(args.packet_size)?; + validate_source_port(source_port)?; + validate_tui_refresh_rate(tui_refresh_rate)?; + validate_report_cycles(args.report_cycles)?; + validate_dns(args.dns_resolve_method, args.dns_lookup_as_info)?; + Ok(Self { + targets: args.targets, + protocol, + first_ttl: args.first_ttl, + max_ttl: args.max_ttl, + min_round_duration, + max_round_duration, + grace_duration, + max_inflight: args.max_inflight, + initial_sequence: args.initial_sequence, + read_timeout, + packet_size: args.packet_size, + payload_pattern: args.payload_pattern, + source_port, + destination_port: args.port, + dns_timeout, + dns_resolve_method: args.dns_resolve_method, + dns_lookup_as_info: args.dns_lookup_as_info, + tui_max_samples: args.tui_max_samples, + tui_preserve_screen: args.tui_preserve_screen, + tui_refresh_rate, + tui_address_mode: args.tui_address_mode, + max_addrs: args.tui_max_addresses_per_hop, + mode: args.mode, + report_cycles: args.report_cycles, + max_rounds, + }) + } +} + /// We only allow multiple targets to be specified for the Tui and for `Icmp` tracing. -pub fn validate_multi(mode: Mode, protocol: TraceProtocol, targets: &[String]) { +pub fn validate_multi( + mode: Mode, + protocol: TraceProtocol, + targets: &[String], +) -> anyhow::Result<()> { match (mode, protocol) { (Mode::Stream | Mode::Pretty | Mode::Markdown | Mode::Csv | Mode::Json, _) if targets.len() > 1 => { - eprintln!("only a single target may be specified for this mode"); - exit(-1); - } - (_, TraceProtocol::Tcp | TraceProtocol::Udp) if targets.len() > 1 => { - eprintln!("only a single target may be specified for TCP and UDP tracing"); - exit(-1); + Err(anyhow!( + "only a single target may be specified for this mode" + )) } - _ => {} + (_, TraceProtocol::Tcp | TraceProtocol::Udp) if targets.len() > 1 => Err(anyhow!( + "only a single target may be specified for TCP and UDP tracing" + )), + _ => Ok(()), } } -/// Validate `report_cycles` -pub fn validate_report_cycles(report_cycles: usize) { - if report_cycles == 0 { - eprintln!( - "report_cycles ({}) must be greater than zero", - report_cycles - ); - exit(-1); +/// Validate `first_ttl` and `max_ttl`. +pub fn validate_ttl(first_ttl: u8, max_ttl: u8) -> anyhow::Result<()> { + if (first_ttl as usize) < 1 || (first_ttl as usize) > MAX_HOPS { + Err(anyhow!( + "first_ttl ({first_ttl}) must be in the range 1..{MAX_HOPS}" + )) + } else if (max_ttl as usize) < 1 || (max_ttl as usize) > MAX_HOPS { + Err(anyhow!( + "max_ttl ({max_ttl}) must be in the range 1..{MAX_HOPS}" + )) + } else if first_ttl > max_ttl { + Err(anyhow!( + "first_ttl ({first_ttl}) must be less than or equal to max_ttl ({max_ttl})" + )) + } else { + Ok(()) } } -/// Validate `tui_refresh_rate` -pub fn validate_tui_refresh_rate(tui_refresh_rate: Duration) { - if tui_refresh_rate < TUI_MIN_REFRESH_RATE_MS || tui_refresh_rate > TUI_MAX_REFRESH_RATE_MS { - eprintln!( - "tui_refresh_rate ({:?}) must be between {:?} and {:?} inclusive", - tui_refresh_rate, TUI_MIN_REFRESH_RATE_MS, TUI_MAX_REFRESH_RATE_MS - ); - exit(-1); +/// Validate `max_inflight`. +pub fn validate_max_inflight(max_inflight: u8) -> anyhow::Result<()> { + if max_inflight == 0 { + Err(anyhow!( + "max_inflight ({}) must be greater than zero", + max_inflight + )) + } else { + Ok(()) } } -/// Validate `grace_duration` -pub fn validate_grace_duration(grace_duration: Duration) { - if grace_duration < MIN_GRACE_DURATION_MS || grace_duration > MAX_GRACE_DURATION_MS { - eprintln!( - "grace_duration ({:?}) must be between {:?} and {:?} inclusive", - grace_duration, MIN_GRACE_DURATION_MS, MAX_GRACE_DURATION_MS - ); - exit(-1); +/// Validate `read_timeout`. +pub fn validate_read_timeout(read_timeout: Duration) -> anyhow::Result<()> { + if read_timeout < MIN_READ_TIMEOUT_MS || read_timeout > MAX_READ_TIMEOUT_MS { + Err(anyhow!( + "read_timeout ({:?}) must be between {:?} and {:?} inclusive", + read_timeout, + MIN_READ_TIMEOUT_MS, + MAX_READ_TIMEOUT_MS + )) + } else { + Ok(()) } } -/// Validate `packet_size` -pub fn validate_packet_size(packet_size: u16) { - if !(MIN_PACKET_SIZE..=MAX_PACKET_SIZE).contains(&packet_size) { - eprintln!( - "packet_size ({}) must be between {} and {} inclusive", - packet_size, MIN_PACKET_SIZE, MAX_PACKET_SIZE - ); - exit(-1); +/// Validate `min_round_duration` and `max_round_duration`. +pub fn validate_round_duration( + min_round_duration: Duration, + max_round_duration: Duration, +) -> anyhow::Result<()> { + if min_round_duration > max_round_duration { + Err(anyhow!( + "max_round_duration ({:?}) must not be less than min_round_duration ({:?})", + max_round_duration, + min_round_duration + )) + } else { + Ok(()) } } -/// Validate `source_port` -pub fn validate_source_port(source_port: u16) { - if source_port < 1024 { - eprintln!("source_port ({}) must be >= 1024", source_port); - exit(-1); +/// Validate `grace_duration`. +pub fn validate_grace_duration(grace_duration: Duration) -> anyhow::Result<()> { + if grace_duration < MIN_GRACE_DURATION_MS || grace_duration > MAX_GRACE_DURATION_MS { + Err(anyhow!( + "grace_duration ({:?}) must be between {:?} and {:?} inclusive", + grace_duration, + MIN_GRACE_DURATION_MS, + MAX_GRACE_DURATION_MS + )) + } else { + Ok(()) } } -/// Validate `min_round_duration` and `max_round_duration` -pub fn validate_round_duration(min_round_duration: Duration, max_round_duration: Duration) { - if min_round_duration > max_round_duration { - eprintln!( - "max_round_duration ({:?}) must not be less than min_round_duration ({:?})", - max_round_duration, min_round_duration - ); - exit(-1); +/// Validate `packet_size`. +pub fn validate_packet_size(packet_size: u16) -> anyhow::Result<()> { + if (MIN_PACKET_SIZE..=MAX_PACKET_SIZE).contains(&packet_size) { + Ok(()) + } else { + Err(anyhow!( + "packet_size ({}) must be between {} and {} inclusive", + packet_size, + MIN_PACKET_SIZE, + MAX_PACKET_SIZE + )) } } -/// Validate `read_timeout` -pub fn validate_read_timeout(read_timeout: Duration) { - if read_timeout < MIN_READ_TIMEOUT_MS || read_timeout > MAX_READ_TIMEOUT_MS { - eprintln!( - "read_timeout ({:?}) must be between {:?} and {:?} inclusive", - read_timeout, MIN_READ_TIMEOUT_MS, MAX_READ_TIMEOUT_MS - ); - exit(-1); +/// Validate `source_port`. +pub fn validate_source_port(source_port: u16) -> anyhow::Result<()> { + if source_port < 1024 { + Err(anyhow!("source_port ({}) must be >= 1024", source_port)) + } else { + Ok(()) } } -/// Validate `max_inflight` -pub fn validate_max_inflight(max_inflight: u8) { - if max_inflight == 0 { - eprintln!("max_inflight ({}) must be greater than zero", max_inflight); - exit(-1); +/// Validate `tui_refresh_rate`. +pub fn validate_tui_refresh_rate(tui_refresh_rate: Duration) -> anyhow::Result<()> { + if tui_refresh_rate < TUI_MIN_REFRESH_RATE_MS || tui_refresh_rate > TUI_MAX_REFRESH_RATE_MS { + Err(anyhow!( + "tui_refresh_rate ({:?}) must be between {:?} and {:?} inclusive", + tui_refresh_rate, + TUI_MIN_REFRESH_RATE_MS, + TUI_MAX_REFRESH_RATE_MS + )) + } else { + Ok(()) } } -/// Validate `first_ttl` and `max_ttl` -pub fn validate_ttl(first_ttl: u8, max_ttl: u8) { - if (first_ttl as usize) < 1 || (first_ttl as usize) > MAX_HOPS { - eprintln!("first_ttl ({first_ttl}) must be in the range 1..{MAX_HOPS}"); - exit(-1); - } - if (max_ttl as usize) < 1 || (max_ttl as usize) > MAX_HOPS { - eprintln!("max_ttl ({max_ttl}) must be in the range 1..{MAX_HOPS}"); - exit(-1); - } - if first_ttl > max_ttl { - eprintln!("first_ttl ({first_ttl}) must be less than or equal to max_ttl ({max_ttl})"); - exit(-1); +/// Validate `report_cycles`. +pub fn validate_report_cycles(report_cycles: usize) -> anyhow::Result<()> { + if report_cycles == 0 { + Err(anyhow!( + "report_cycles ({}) must be greater than zero", + report_cycles + )) + } else { + Ok(()) } } -/// Validate `dns_resolve_method` and `dns_lookup_as_info` -pub fn validate_dns(dns_resolve_method: DnsResolveMethod, dns_lookup_as_info: bool) { +/// Validate `dns_resolve_method` and `dns_lookup_as_info`. +pub fn validate_dns( + dns_resolve_method: DnsResolveMethod, + dns_lookup_as_info: bool, +) -> anyhow::Result<()> { match dns_resolve_method { - DnsResolveMethod::System if dns_lookup_as_info => { - eprintln!("AS lookup not supported by resolver `system` (use '-r' to choose another resolver)"); - exit(-1); - } - _ => {} + DnsResolveMethod::System if dns_lookup_as_info => Err(anyhow!( + "AS lookup not supported by resolver `system` (use '-r' to choose another resolver)" + )), + _ => Ok(()), } } diff --git a/src/frontend.rs b/src/frontend.rs index ed74079a..66019382 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -1,7 +1,7 @@ use crate::backend::Hop; use crate::config::{AddressMode, DnsResolveMethod}; use crate::dns::{DnsEntry, Resolved}; -use crate::{DnsResolver, Trace}; +use crate::{DnsResolver, Trace, TraceInfo}; use chrono::SecondsFormat; use crossterm::event::KeyModifiers; use crossterm::{ @@ -10,11 +10,9 @@ use crossterm::{ terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; use itertools::Itertools; -use parking_lot::RwLock; use std::collections::BTreeMap; use std::io; use std::net::IpAddr; -use std::sync::Arc; use std::time::{Duration, SystemTime}; use trippy::tracing::TracerProtocol; use tui::layout::{Alignment, Direction, Rect}; @@ -76,47 +74,6 @@ const HELP_LINES: [&str; 14] = [ "q - quit", ]; -/// Information about a `Trace` needed for the Tui. -#[derive(Debug, Clone)] -pub struct TuiTraceInfo { - pub data: Arc>, - pub target_hostname: String, - pub target_addr: IpAddr, - pub target_port: u16, - pub protocol: TracerProtocol, - pub first_ttl: u8, - pub max_ttl: u8, - pub grace_duration: Duration, - pub min_round_duration: Duration, -} - -impl TuiTraceInfo { - #[allow(clippy::too_many_arguments)] - pub fn new( - data: Arc>, - target_hostname: String, - target_addr: IpAddr, - target_port: u16, - protocol: TracerProtocol, - first_ttl: u8, - max_ttl: u8, - grace_duration: Duration, - min_round_duration: Duration, - ) -> Self { - Self { - data, - target_hostname, - target_addr, - target_port, - protocol, - first_ttl, - max_ttl, - grace_duration, - min_round_duration, - } - } -} - /// Tui configuration. #[derive(Debug)] pub struct TuiConfig { @@ -156,7 +113,7 @@ impl TuiConfig { struct TuiApp { selected_tracer_data: Trace, - trace_info: Vec, + trace_info: Vec, tui_config: TuiConfig, table_state: TableState, trace_selected: usize, @@ -166,7 +123,7 @@ struct TuiApp { } impl TuiApp { - fn new(tui_config: TuiConfig, resolver: DnsResolver, trace_info: Vec) -> Self { + fn new(tui_config: TuiConfig, resolver: DnsResolver, trace_info: Vec) -> Self { Self { selected_tracer_data: Trace::new(tui_config.max_samples), trace_info, @@ -192,7 +149,7 @@ impl TuiApp { Trace::new(self.tui_config.max_samples); } - fn tracer_config(&self) -> &TuiTraceInfo { + fn tracer_config(&self) -> &TraceInfo { &self.trace_info[self.trace_selected] } @@ -310,7 +267,7 @@ impl TuiApp { /// Run the frontend TUI. pub fn run_frontend( - traces: Vec, + traces: Vec, tui_config: TuiConfig, resolver: DnsResolver, ) -> anyhow::Result<()> { @@ -334,7 +291,7 @@ pub fn run_frontend( fn run_app( terminal: &mut Terminal, - trace_info: Vec, + trace_info: Vec, tui_config: TuiConfig, resolver: DnsResolver, ) -> io::Result<()> { diff --git a/src/main.rs b/src/main.rs index bdaa795f..75aaf9ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,17 +12,9 @@ use crate::backend::Trace; use crate::caps::{drop_caps, ensure_caps}; -use crate::config::{ - validate_dns, validate_grace_duration, validate_max_inflight, validate_multi, - validate_packet_size, validate_read_timeout, validate_report_cycles, validate_round_duration, - validate_source_port, validate_ttl, validate_tui_refresh_rate, Mode, TraceProtocol, -}; +use crate::config::{Mode, TrippyConfig}; use crate::dns::{DnsResolver, DnsResolverConfig}; -use crate::frontend::{TuiConfig, TuiTraceInfo}; -use crate::report::{ - run_report_csv, run_report_json, run_report_stream, run_report_table_markdown, - run_report_table_pretty, -}; +use crate::frontend::TuiConfig; use anyhow::anyhow; use clap::Parser; use config::Args; @@ -30,7 +22,8 @@ use parking_lot::RwLock; use std::net::IpAddr; use std::sync::Arc; use std::thread; -use trippy::tracing::{TracerChannel, TracerConfig}; +use std::time::Duration; +use trippy::tracing::{TracerChannel, TracerConfig, TracerProtocol}; mod backend; mod caps; @@ -39,137 +32,187 @@ mod dns; mod frontend; mod report; -#[allow(clippy::too_many_lines)] fn main() -> anyhow::Result<()> { let pid = u16::try_from(std::process::id() % u32::from(u16::MAX))?; - let args = Args::parse(); - let targets = args.targets; - let protocol = match args.protocol { - TraceProtocol::Icmp => trippy::tracing::TracerProtocol::Icmp, - TraceProtocol::Udp => trippy::tracing::TracerProtocol::Udp, - TraceProtocol::Tcp => trippy::tracing::TracerProtocol::Tcp, - }; - let read_timeout = humantime::parse_duration(&args.read_timeout)?; - let min_round_duration = humantime::parse_duration(&args.min_round_duration)?; - let max_round_duration = humantime::parse_duration(&args.max_round_duration)?; - let grace_duration = humantime::parse_duration(&args.grace_duration)?; - let source_port = args.source_port.unwrap_or_else(|| pid.max(1024)); - let tui_refresh_rate = humantime::parse_duration(&args.tui_refresh_rate)?; - let report_cycles = args.report_cycles; - let dns_timeout = humantime::parse_duration(&args.dns_timeout)?; - let max_rounds = match args.mode { - Mode::Stream | Mode::Tui => None, - Mode::Pretty | Mode::Markdown | Mode::Csv | Mode::Json => Some(report_cycles), - }; - validate_multi(args.mode, args.protocol, &targets); - validate_ttl(args.first_ttl, args.max_ttl); - validate_max_inflight(args.max_inflight); - validate_read_timeout(read_timeout); - validate_round_duration(min_round_duration, max_round_duration); - validate_grace_duration(grace_duration); - validate_packet_size(args.packet_size); - validate_source_port(source_port); - validate_tui_refresh_rate(tui_refresh_rate); - validate_report_cycles(args.report_cycles); - validate_dns(args.dns_resolve_method, args.dns_lookup_as_info); - let resolver = - DnsResolver::start(DnsResolverConfig::new(args.dns_resolve_method, dns_timeout))?; + let cfg = TrippyConfig::try_from((Args::parse(), pid))?; ensure_caps()?; - let traces: Vec<_> = targets + let resolver = DnsResolver::start(DnsResolverConfig::new( + cfg.dns_resolve_method, + cfg.dns_timeout, + ))?; + let traces: Vec<_> = cfg + .targets .iter() - .map(|target| { - let target_addr: IpAddr = resolver - .lookup(target) - .map_err(|e| anyhow!("failed to resolve target: {} ({})", target, e))? - .into_iter() - .find(|addr| matches!(addr, IpAddr::V4(_))) - .unwrap(); - let trace_data = Arc::new(RwLock::new(Trace::new(args.tui_max_samples))); - Ok(TuiTraceInfo::new( - trace_data, - target.clone(), - target_addr, - args.port, - protocol, - args.first_ttl, - args.max_ttl, - grace_duration, - min_round_duration, - )) - }) + .map(|target| make_trace_info(&cfg, &resolver, target)) .collect::>>()?; for (i, info) in traces.iter().enumerate() { - let tracer_config = TracerConfig::new( - info.target_addr, - protocol, - max_rounds, - pid + i as u16, - args.first_ttl, - args.max_ttl, - grace_duration, - args.max_inflight, - args.initial_sequence, - read_timeout, - min_round_duration, - max_round_duration, - args.packet_size, - args.payload_pattern, - source_port, - args.port, - )?; + let trace_identifier = pid + i as u16; + let tracer_config = make_tracer_config(&cfg, info.target_addr, trace_identifier)?; start_backend(tracer_config, info.data.clone())?; } drop_caps()?; + run_frontend(&cfg, resolver, traces)?; + Ok(()) +} + +/// Create a network channel in a thread and drop all capabilities. +fn start_backend( + tracer_config: TracerConfig, + trace_data: Arc>, +) -> anyhow::Result<()> { + let channel = TracerChannel::connect(&tracer_config)?; + thread::Builder::new() + .name(format!("tracer-{}", tracer_config.trace_identifier.0)) + .spawn(move || { + drop_caps().expect("failed to drop capabilities in tracer thread"); + backend::run_backend(&tracer_config, channel, trace_data).expect("backend failed"); + })?; + Ok(()) +} + +/// Run the TUI, stream or report. +fn run_frontend( + args: &TrippyConfig, + resolver: DnsResolver, + traces: Vec, +) -> anyhow::Result<()> { match args.mode { Mode::Tui => { - let tui_config = TuiConfig::new( - tui_refresh_rate, - args.tui_preserve_screen, - args.tui_address_mode, - args.dns_lookup_as_info, - args.tui_max_addresses_per_hop, - args.tui_max_samples, - ); + let tui_config = make_tui_config(args); frontend::run_frontend(traces, tui_config, resolver)?; } - Mode::Stream => run_report_stream( + Mode::Stream => report::run_report_stream( &traces[0].target_hostname, traces[0].target_addr, traces[0].min_round_duration, &traces[0].data, ), - Mode::Csv => run_report_csv( + Mode::Csv => report::run_report_csv( &traces[0].target_hostname, traces[0].target_addr, - report_cycles, + args.report_cycles, &resolver, &traces[0].data, ), - Mode::Json => run_report_json( + Mode::Json => report::run_report_json( &traces[0].target_hostname, traces[0].target_addr, - report_cycles, + args.report_cycles, &resolver, &traces[0].data, ), - Mode::Pretty => run_report_table_pretty(report_cycles, &resolver, &traces[0].data), - Mode::Markdown => run_report_table_markdown(report_cycles, &resolver, &traces[0].data), + Mode::Pretty => { + report::run_report_table_pretty(args.report_cycles, &resolver, &traces[0].data); + } + Mode::Markdown => { + report::run_report_table_markdown(args.report_cycles, &resolver, &traces[0].data); + } } Ok(()) } -/// Create the network channel and then dropping all capabilities. -fn start_backend( - tracer_config: TracerConfig, - trace_data: Arc>, -) -> anyhow::Result<()> { - let channel = TracerChannel::connect(&tracer_config)?; - thread::Builder::new() - .name(format!("tracer-{}", tracer_config.trace_identifier.0)) - .spawn(move || { - drop_caps().expect("failed to drop capabilities in tracer thread"); - backend::run_backend(&tracer_config, channel, trace_data).expect("backend failed"); - })?; +/// Make the tracer configuration. +fn make_tracer_config( + args: &TrippyConfig, + target_addr: IpAddr, + trace_identifier: u16, +) -> anyhow::Result { + Ok(TracerConfig::new( + target_addr, + args.protocol, + args.max_rounds, + trace_identifier, + args.first_ttl, + args.max_ttl, + args.grace_duration, + args.max_inflight, + args.initial_sequence, + args.read_timeout, + args.min_round_duration, + args.max_round_duration, + args.packet_size, + args.payload_pattern, + args.source_port, + args.destination_port, + )?) +} - Ok(()) +/// Make the per-trace information. +fn make_trace_info( + args: &TrippyConfig, + resolver: &DnsResolver, + target: &str, +) -> anyhow::Result { + let target_addr: IpAddr = resolver + .lookup(target) + .map_err(|e| anyhow!("failed to resolve target: {} ({})", target, e))? + .into_iter() + .find(|addr| matches!(addr, IpAddr::V4(_))) + .unwrap(); + let trace_data = Arc::new(RwLock::new(Trace::new(args.tui_max_samples))); + Ok(TraceInfo::new( + trace_data, + target.to_string(), + target_addr, + args.destination_port, + args.protocol, + args.first_ttl, + args.max_ttl, + args.grace_duration, + args.min_round_duration, + )) +} + +/// Make the TUI configuration. +fn make_tui_config(args: &TrippyConfig) -> TuiConfig { + TuiConfig::new( + args.tui_refresh_rate, + args.tui_preserve_screen, + args.tui_address_mode, + args.dns_lookup_as_info, + args.max_addrs, + args.tui_max_samples, + ) +} + +/// Information about a `Trace` needed for the Tui, stream and reports. +#[derive(Debug, Clone)] +pub struct TraceInfo { + pub data: Arc>, + pub target_hostname: String, + pub target_addr: IpAddr, + pub target_port: u16, + pub protocol: TracerProtocol, + pub first_ttl: u8, + pub max_ttl: u8, + pub grace_duration: Duration, + pub min_round_duration: Duration, +} + +impl TraceInfo { + #[allow(clippy::too_many_arguments)] + #[must_use] + pub fn new( + data: Arc>, + target_hostname: String, + target_addr: IpAddr, + target_port: u16, + protocol: TracerProtocol, + first_ttl: u8, + max_ttl: u8, + grace_duration: Duration, + min_round_duration: Duration, + ) -> Self { + Self { + data, + target_hostname, + target_addr, + target_port, + protocol, + first_ttl, + max_ttl, + grace_duration, + min_round_duration, + } + } } From e520a420e188c97a018535a62dfcb235673d09cf Mon Sep 17 00:00:00 2001 From: FujiApple Date: Mon, 16 May 2022 13:00:08 +0800 Subject: [PATCH 5/6] refactor: simplify report parameter passing --- src/main.rs | 36 ++++-------------------- src/report.rs | 78 +++++++++++++++++++-------------------------------- 2 files changed, 35 insertions(+), 79 deletions(-) diff --git a/src/main.rs b/src/main.rs index 75aaf9ef..f848c72e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,36 +77,12 @@ fn run_frontend( traces: Vec, ) -> anyhow::Result<()> { match args.mode { - Mode::Tui => { - let tui_config = make_tui_config(args); - frontend::run_frontend(traces, tui_config, resolver)?; - } - Mode::Stream => report::run_report_stream( - &traces[0].target_hostname, - traces[0].target_addr, - traces[0].min_round_duration, - &traces[0].data, - ), - Mode::Csv => report::run_report_csv( - &traces[0].target_hostname, - traces[0].target_addr, - args.report_cycles, - &resolver, - &traces[0].data, - ), - Mode::Json => report::run_report_json( - &traces[0].target_hostname, - traces[0].target_addr, - args.report_cycles, - &resolver, - &traces[0].data, - ), - Mode::Pretty => { - report::run_report_table_pretty(args.report_cycles, &resolver, &traces[0].data); - } - Mode::Markdown => { - report::run_report_table_markdown(args.report_cycles, &resolver, &traces[0].data); - } + Mode::Tui => frontend::run_frontend(traces, make_tui_config(args), resolver)?, + Mode::Stream => report::run_report_stream(&traces[0]), + Mode::Csv => report::run_report_csv(&traces[0], args.report_cycles, &resolver), + Mode::Json => report::run_report_json(&traces[0], args.report_cycles, &resolver), + Mode::Pretty => report::run_report_table_pretty(&traces[0], args.report_cycles, &resolver), + Mode::Markdown => report::run_report_table_md(&traces[0], args.report_cycles, &resolver), } Ok(()) } diff --git a/src/report.rs b/src/report.rs index 89e753aa..7c3dd942 100644 --- a/src/report.rs +++ b/src/report.rs @@ -1,23 +1,16 @@ -use crate::{DnsResolver, Trace}; +use crate::{DnsResolver, Trace, TraceInfo}; use comfy_table::presets::{ASCII_MARKDOWN, UTF8_FULL}; use comfy_table::{ContentArrangement, Table}; use itertools::Itertools; use parking_lot::RwLock; use serde::{Serialize, Serializer}; -use std::net::IpAddr; use std::sync::Arc; use std::thread::sleep; use std::time::Duration; /// Generate a CSV report of trace data. -pub fn run_report_csv( - hostname: &str, - target_addr: IpAddr, - report_cycles: usize, - resolver: &DnsResolver, - trace_data: &Arc>, -) { - let trace = wait_for_round(trace_data, report_cycles); +pub fn run_report_csv(info: &TraceInfo, report_cycles: usize, resolver: &DnsResolver) { + let trace = wait_for_round(&info.data, report_cycles); println!("Target,TargetIp,Hop,Addrs,Loss%,Snt,Recv,Last,Avg,Best,Wrst,StdDev,"); for hop in trace.hops().iter() { let ttl = hop.ttl(); @@ -43,7 +36,18 @@ pub fn run_report_csv( let loss_pct = hop.loss_pct(); println!( "{},{},{},{},{:.1}%,{},{},{},{:.1},{},{},{:.1}", - hostname, target_addr, ttl, host, loss_pct, sent, recv, last, avg, best, worst, stddev + info.target_hostname, + info.target_addr, + ttl, + host, + loss_pct, + sent, + recv, + last, + avg, + best, + worst, + stddev ); } } @@ -94,14 +98,8 @@ where } /// Generate a CSV report of trace data. -pub fn run_report_json( - hostname: &str, - target_addr: IpAddr, - report_cycles: usize, - resolver: &DnsResolver, - trace_data: &Arc>, -) { - let trace = wait_for_round(trace_data, report_cycles); +pub fn run_report_json(info: &TraceInfo, report_cycles: usize, resolver: &DnsResolver) { + let trace = wait_for_round(&info.data, report_cycles); let hops: Vec = trace .hops() .iter() @@ -131,8 +129,8 @@ pub fn run_report_json( let report = Report { info: ReportInfo { target: Host { - ip: target_addr.to_string(), - hostname: hostname.to_string(), + ip: info.target_addr.to_string(), + hostname: info.target_hostname.to_string(), }, }, hops, @@ -141,30 +139,17 @@ pub fn run_report_json( } /// Generate a markdown table report of trace data. -pub fn run_report_table_markdown( - report_cycles: usize, - resolver: &DnsResolver, - trace_data: &Arc>, -) { - run_report_table(report_cycles, resolver, trace_data, ASCII_MARKDOWN); +pub fn run_report_table_md(info: &TraceInfo, report_cycles: usize, resolver: &DnsResolver) { + run_report_table(info, report_cycles, resolver, ASCII_MARKDOWN); } /// Generate a pretty table report of trace data. -pub fn run_report_table_pretty( - report_cycles: usize, - resolver: &DnsResolver, - trace_data: &Arc>, -) { - run_report_table(report_cycles, resolver, trace_data, UTF8_FULL); +pub fn run_report_table_pretty(info: &TraceInfo, report_cycles: usize, resolver: &DnsResolver) { + run_report_table(info, report_cycles, resolver, UTF8_FULL); } -fn run_report_table( - report_cycles: usize, - resolver: &DnsResolver, - trace_data: &Arc>, - preset: &str, -) { - let trace = wait_for_round(trace_data, report_cycles); +fn run_report_table(info: &TraceInfo, report_cycles: usize, resolver: &DnsResolver, preset: &str) { + let trace = wait_for_round(&info.data, report_cycles); let columns = vec![ "Hop", "Addrs", "Loss%", "Snt", "Recv", "Last", "Avg", "Best", "Wrst", "StdDev", ]; @@ -206,15 +191,10 @@ fn run_report_table( } /// Display a continuous stream of trace data. -pub fn run_report_stream( - hostname: &str, - target_addr: IpAddr, - interval: Duration, - trace_data: &Arc>, -) { - println!("Tracing to {} ({})", hostname, target_addr); +pub fn run_report_stream(info: &TraceInfo) { + println!("Tracing to {} ({})", info.target_hostname, info.target_addr); loop { - let trace_data = trace_data.read().clone(); + let trace_data = &info.data.read().clone(); for hop in trace_data.hops() { let ttl = hop.ttl(); let addrs = hop.addrs().collect::>(); @@ -240,7 +220,7 @@ pub fn run_report_stream( ttl, addrs, loss_pct, sent, recv, last, best, worst, avg, stddev ); } - sleep(interval); + sleep(info.min_round_duration); } } From c08a6015aa0ae138dd9b4c9891daa598be4f9284 Mon Sep 17 00:00:00 2001 From: FujiApple Date: Mon, 16 May 2022 15:22:32 +0800 Subject: [PATCH 6/6] feat: show source address and port in Tui --- src/frontend.rs | 81 ++++++++++++++++++++++++------- src/main.rs | 109 ++++++++++++++++++++++++++++-------------- src/tracing.rs | 2 +- src/tracing/config.rs | 2 +- src/tracing/net.rs | 52 ++++++++++++++++++-- 5 files changed, 186 insertions(+), 60 deletions(-) diff --git a/src/frontend.rs b/src/frontend.rs index 66019382..eda9ffd2 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -449,24 +449,9 @@ fn render_header(f: &mut Frame<'_, B>, app: &mut TuiApp, rect: Rect) .tui_config .max_addrs .map_or_else(|| String::from("auto"), |m| m.to_string()); - let target = match app.tracer_config().protocol { - TracerProtocol::Icmp => { - format!( - "{} ({})", - app.tracer_config().target_hostname, - app.tracer_config().target_addr - ) - } - TracerProtocol::Udp | TracerProtocol::Tcp => { - format!( - "{}:{} ({}:{})", - app.tracer_config().target_hostname, - app.tracer_config().target_port, - app.tracer_config().target_addr, - app.tracer_config().target_port - ) - } - }; + let source = render_source(app); + let dest = render_destination(app); + let target = format!("{} -> {}", source, dest); let left_spans = vec![ Spans::from(vec![ Span::styled("Target: ", Style::default().add_modifier(Modifier::BOLD)), @@ -503,6 +488,66 @@ fn render_header(f: &mut Frame<'_, B>, app: &mut TuiApp, rect: Rect) f.render_widget(left, rect); } +/// Render the source address of the trace. +fn render_source(app: &mut TuiApp) -> String { + let source = match app.tracer_config().protocol { + TracerProtocol::Icmp => { + format!( + "{} ({})", + app.resolver.reverse_lookup(app.tracer_config().source_addr), + app.tracer_config().source_addr + ) + } + TracerProtocol::Udp => { + format!( + "{}:{} ({}:{})", + app.resolver.reverse_lookup(app.tracer_config().source_addr), + app.tracer_config().source_port, + app.tracer_config().source_addr, + app.tracer_config().source_port, + ) + } + TracerProtocol::Tcp => { + format!( + "{}:* ({}:*)", + app.resolver.reverse_lookup(app.tracer_config().source_addr), + app.tracer_config().source_addr, + ) + } + }; + source +} + +/// Render the destination address. +fn render_destination(app: &mut TuiApp) -> String { + let dest = match app.tracer_config().protocol { + TracerProtocol::Icmp => { + format!( + "{} ({})", + app.tracer_config().target_hostname, + app.tracer_config().target_addr + ) + } + TracerProtocol::Udp => { + format!( + "{}:* ({}:*)", + app.tracer_config().target_hostname, + app.tracer_config().target_addr, + ) + } + TracerProtocol::Tcp => { + format!( + "{}:{} ({}:{})", + app.tracer_config().target_hostname, + app.tracer_config().target_port, + app.tracer_config().target_addr, + app.tracer_config().target_port + ) + } + }; + dest +} + /// Format the `DnsResolveMethod`. fn format_dns_method(resolve_method: DnsResolveMethod) -> String { match resolve_method { diff --git a/src/main.rs b/src/main.rs index f848c72e..d21fb6d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,7 @@ use crate::caps::{drop_caps, ensure_caps}; use crate::config::{Mode, TrippyConfig}; use crate::dns::{DnsResolver, DnsResolverConfig}; use crate::frontend::TuiConfig; -use anyhow::anyhow; +use anyhow::{anyhow, Error}; use clap::Parser; use config::Args; use parking_lot::RwLock; @@ -23,7 +23,7 @@ use std::net::IpAddr; use std::sync::Arc; use std::thread; use std::time::Duration; -use trippy::tracing::{TracerChannel, TracerConfig, TracerProtocol}; +use trippy::tracing::{TracerChannel, TracerChannelConfig, TracerConfig, TracerProtocol}; mod backend; mod caps; @@ -35,39 +35,56 @@ mod report; fn main() -> anyhow::Result<()> { let pid = u16::try_from(std::process::id() % u32::from(u16::MAX))?; let cfg = TrippyConfig::try_from((Args::parse(), pid))?; - ensure_caps()?; let resolver = DnsResolver::start(DnsResolverConfig::new( cfg.dns_resolve_method, cfg.dns_timeout, ))?; + ensure_caps()?; let traces: Vec<_> = cfg .targets .iter() - .map(|target| make_trace_info(&cfg, &resolver, target)) + .enumerate() + .map(|(i, target_host)| start_tracer(&cfg, target_host, pid + i as u16, &resolver)) .collect::>>()?; - for (i, info) in traces.iter().enumerate() { - let trace_identifier = pid + i as u16; - let tracer_config = make_tracer_config(&cfg, info.target_addr, trace_identifier)?; - start_backend(tracer_config, info.data.clone())?; - } drop_caps()?; run_frontend(&cfg, resolver, traces)?; Ok(()) } -/// Create a network channel in a thread and drop all capabilities. -fn start_backend( - tracer_config: TracerConfig, - trace_data: Arc>, -) -> anyhow::Result<()> { - let channel = TracerChannel::connect(&tracer_config)?; - thread::Builder::new() - .name(format!("tracer-{}", tracer_config.trace_identifier.0)) - .spawn(move || { - drop_caps().expect("failed to drop capabilities in tracer thread"); - backend::run_backend(&tracer_config, channel, trace_data).expect("backend failed"); - })?; - Ok(()) +/// Start a tracer to a given target. +fn start_tracer( + cfg: &TrippyConfig, + target_host: &str, + trace_identifier: u16, + resolver: &DnsResolver, +) -> Result { + let target_addr: IpAddr = resolver + .lookup(target_host) + .map_err(|e| anyhow!("failed to resolve target: {} ({})", target_host, e))? + .into_iter() + .find(|addr| matches!(addr, IpAddr::V4(_))) + .ok_or_else(|| anyhow!("failed to find an IPv4 address for target: {}", target_host))?; + let trace_data = Arc::new(RwLock::new(Trace::new(cfg.tui_max_samples))); + let channel_config = make_channel_config(cfg, target_addr, trace_identifier); + let channel = TracerChannel::connect(&channel_config)?; + let source_addr = channel.src_addr(); + let tracer_config = make_tracer_config(cfg, target_addr, trace_identifier)?; + { + let trace_data = trace_data.clone(); + thread::Builder::new() + .name(format!("tracer-{}", tracer_config.trace_identifier.0)) + .spawn(move || { + drop_caps().expect("failed to drop capabilities in tracer thread"); + backend::run_backend(&tracer_config, channel, trace_data).expect("backend failed"); + })?; + } + Ok(make_trace_info( + cfg, + trace_data, + source_addr, + target_host.to_string(), + target_addr, + )) } /// Run the TUI, stream or report. @@ -113,22 +130,38 @@ fn make_tracer_config( )?) } +/// Make the tracer configuration. +fn make_channel_config( + args: &TrippyConfig, + target_addr: IpAddr, + trace_identifier: u16, +) -> TracerChannelConfig { + TracerChannelConfig::new( + args.protocol, + target_addr, + trace_identifier, + args.packet_size, + args.payload_pattern, + args.source_port, + args.destination_port, + args.read_timeout, + args.min_round_duration, + ) +} + /// Make the per-trace information. fn make_trace_info( args: &TrippyConfig, - resolver: &DnsResolver, - target: &str, -) -> anyhow::Result { - let target_addr: IpAddr = resolver - .lookup(target) - .map_err(|e| anyhow!("failed to resolve target: {} ({})", target, e))? - .into_iter() - .find(|addr| matches!(addr, IpAddr::V4(_))) - .unwrap(); - let trace_data = Arc::new(RwLock::new(Trace::new(args.tui_max_samples))); - Ok(TraceInfo::new( + trace_data: Arc>, + source_addr: IpAddr, + target: String, + target_addr: IpAddr, +) -> TraceInfo { + TraceInfo::new( trace_data, - target.to_string(), + source_addr, + args.source_port, + target, target_addr, args.destination_port, args.protocol, @@ -136,7 +169,7 @@ fn make_trace_info( args.max_ttl, args.grace_duration, args.min_round_duration, - )) + ) } /// Make the TUI configuration. @@ -155,6 +188,8 @@ fn make_tui_config(args: &TrippyConfig) -> TuiConfig { #[derive(Debug, Clone)] pub struct TraceInfo { pub data: Arc>, + pub source_addr: IpAddr, + pub source_port: u16, pub target_hostname: String, pub target_addr: IpAddr, pub target_port: u16, @@ -170,6 +205,8 @@ impl TraceInfo { #[must_use] pub fn new( data: Arc>, + source_addr: IpAddr, + source_port: u16, target_hostname: String, target_addr: IpAddr, target_port: u16, @@ -181,6 +218,8 @@ impl TraceInfo { ) -> Self { Self { data, + source_addr, + source_port, target_hostname, target_addr, target_port, diff --git a/src/tracing.rs b/src/tracing.rs index 4ddc9302..d200545d 100644 --- a/src/tracing.rs +++ b/src/tracing.rs @@ -7,6 +7,6 @@ mod types; mod util; pub use config::{TracerConfig, TracerProtocol}; -pub use net::TracerChannel; +pub use net::{TracerChannel, TracerChannelConfig}; pub use probe::{IcmpPacketType, Probe, ProbeStatus}; pub use tracer::{Tracer, TracerRound}; diff --git a/src/tracing/config.rs b/src/tracing/config.rs index 81a964ef..4000fa99 100644 --- a/src/tracing/config.rs +++ b/src/tracing/config.rs @@ -36,7 +36,7 @@ impl Display for TracerProtocol { } } -/// TODO +/// Tracing algorithm configuration. #[derive(Debug, Copy, Clone)] pub struct TracerConfig { pub target_addr: IpAddr, diff --git a/src/tracing/net.rs b/src/tracing/net.rs index a09190bb..328b0846 100644 --- a/src/tracing/net.rs +++ b/src/tracing/net.rs @@ -2,7 +2,7 @@ use crate::tracing::error::TracerError::AddressNotAvailable; use crate::tracing::error::{TraceResult, TracerError}; use crate::tracing::types::{DestinationPort, PacketSize, PayloadPattern, SourcePort, TraceId}; use crate::tracing::util::Required; -use crate::tracing::{Probe, TracerConfig, TracerProtocol}; +use crate::tracing::{Probe, TracerProtocol}; use arrayvec::ArrayVec; use itertools::Itertools; use nix::sys::select::FdSet; @@ -119,11 +119,53 @@ pub struct TracerChannel { tcp_probes: ArrayVec, } +/// Tracer network channel configuration. +#[derive(Debug, Copy, Clone)] +pub struct TracerChannelConfig { + protocol: TracerProtocol, + target_addr: IpAddr, + identifier: TraceId, + packet_size: PacketSize, + payload_pattern: PayloadPattern, + source_port: SourcePort, + destination_port: DestinationPort, + icmp_read_timeout: Duration, + tcp_connect_timeout: Duration, +} + +impl TracerChannelConfig { + #[allow(clippy::too_many_arguments)] + #[must_use] + pub fn new( + protocol: TracerProtocol, + target_addr: IpAddr, + identifier: u16, + packet_size: u16, + payload_pattern: u8, + source_port: u16, + destination_port: u16, + icmp_read_timeout: Duration, + tcp_connect_timeout: Duration, + ) -> Self { + Self { + protocol, + target_addr, + identifier: TraceId(identifier), + packet_size: PacketSize(packet_size), + payload_pattern: PayloadPattern(payload_pattern), + source_port: SourcePort(source_port), + destination_port: DestinationPort(destination_port), + icmp_read_timeout, + tcp_connect_timeout, + } + } +} + impl TracerChannel { /// Create an `IcmpChannel`. /// /// This operation requires the `CAP_NET_RAW` capability on Linux. - pub fn connect(config: &TracerConfig) -> TraceResult { + pub fn connect(config: &TracerChannelConfig) -> TraceResult { let src_addr = discover_ipv4_addr(config.target_addr, config.destination_port.0)?; let (icmp_tx, icmp_rx) = make_icmp_channel()?; let (udp_tx, _) = make_udp_channel()?; @@ -131,13 +173,13 @@ impl TracerChannel { protocol: config.protocol, src_addr, dest_addr: config.target_addr, - identifier: config.trace_identifier, + identifier: config.identifier, packet_size: config.packet_size, payload_pattern: config.payload_pattern, source_port: config.source_port, destination_port: config.destination_port, - icmp_read_timeout: config.read_timeout, - tcp_connect_timeout: config.min_round_duration, + icmp_read_timeout: config.icmp_read_timeout, + tcp_connect_timeout: config.tcp_connect_timeout, icmp_tx, icmp_rx, udp_tx,