diff --git a/src/config.rs b/src/config.rs index eb7ad878..aed1c55c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,10 +10,9 @@ use std::collections::HashMap; use std::net::IpAddr; use std::str::FromStr; use std::time::Duration; -use trippy::dns::ResolveMethod; +use trippy::dns::{IpAddrFamily, ResolveMethod}; use trippy::tracing::{ - defaults, AddrFamily, IcmpExtensionParseMode, MultipathStrategy, PortDirection, PrivilegeMode, - Protocol, + defaults, IcmpExtensionParseMode, MultipathStrategy, PortDirection, PrivilegeMode, Protocol, }; mod binding; @@ -76,20 +75,28 @@ impl From for ProtocolConfig { } /// The address family. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Deserialize)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, ValueEnum, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum AddressFamilyConfig { - /// Internet Protocol V4 + /// Ipv4 only. Ipv4, - /// Internet Protocol V6 + /// Ipv6 only. Ipv6, + /// Ipv6 with a fallback to Ipv4 + #[serde(rename = "ipv6-then-ipv4")] + Ipv6ThenIpv4, + /// Ipv4 with a fallback to Ipv6 + #[serde(rename = "ipv4-then-ipv6")] + Ipv4ThenIpv6, } -impl From for AddressFamilyConfig { - fn from(value: AddrFamily) -> Self { +impl From for AddressFamilyConfig { + fn from(value: IpAddrFamily) -> Self { match value { - AddrFamily::Ipv4 => Self::Ipv4, - AddrFamily::Ipv6 => Self::Ipv6, + IpAddrFamily::Ipv4Only => Self::Ipv4, + IpAddrFamily::Ipv6Only => Self::Ipv6, + IpAddrFamily::Ipv6thenIpv4 => Self::Ipv6ThenIpv4, + IpAddrFamily::Ipv4thenIpv6 => Self::Ipv4ThenIpv6, } } } @@ -270,7 +277,7 @@ impl TrippyAction { pub struct TrippyConfig { pub targets: Vec, pub protocol: Protocol, - pub addr_family: AddrFamily, + pub addr_family: IpAddrFamily, pub first_ttl: u8, pub max_ttl: u8, pub min_round_duration: Duration, @@ -378,6 +385,11 @@ impl TrippyConfig { cfg_file_strategy.protocol, ProtocolConfig::from(defaults::DEFAULT_STRATEGY_PROTOCOL), ); + let addr_family_cfg = cfg_layer( + args.addr_family, + cfg_file_strategy.addr_family, + constants::DEFAULT_ADDR_FAMILY, + ); let target_port = cfg_layer_opt(args.target_port, cfg_file_strategy.target_port); let source_port = cfg_layer_opt(args.source_port, cfg_file_strategy.source_port); let source_address = cfg_layer_opt(args.source_address, cfg_file_strategy.source_address); @@ -541,19 +553,42 @@ impl TrippyConfig { .map_err(|_| anyhow!("invalid source IP address format: {}", addr)) }) .transpose()?; - let addr_family = match (args.ipv4, args.ipv6, cfg_file_strategy.addr_family) { - (false, false, None) => defaults::DEFAULT_ADDRESS_FAMILY, - (false, false, Some(AddressFamilyConfig::Ipv4)) | (true, _, _) => AddrFamily::Ipv4, - (false, false, Some(AddressFamilyConfig::Ipv6)) | (_, true, _) => AddrFamily::Ipv6, + + #[allow(clippy::match_same_arms)] + let addr_family = match ( + args.ipv4, + args.ipv6, + addr_family_cfg, + multipath_strategy_cfg, + ) { + (false, false, AddressFamilyConfig::Ipv4, _) => IpAddrFamily::Ipv4Only, + (false, false, AddressFamilyConfig::Ipv6, _) => IpAddrFamily::Ipv6Only, + // we "downgrade" to `Ipv4Only` for `Dublin` rather than fail. + (false, false, AddressFamilyConfig::Ipv4ThenIpv6, MultipathStrategyConfig::Dublin) => { + IpAddrFamily::Ipv4Only + } + (false, false, AddressFamilyConfig::Ipv6ThenIpv4, MultipathStrategyConfig::Dublin) => { + IpAddrFamily::Ipv4Only + } + (false, false, AddressFamilyConfig::Ipv4ThenIpv6, _) => IpAddrFamily::Ipv4thenIpv6, + (false, false, AddressFamilyConfig::Ipv6ThenIpv4, _) => IpAddrFamily::Ipv6thenIpv4, + (true, _, _, _) => IpAddrFamily::Ipv4Only, + (_, true, _, _) => IpAddrFamily::Ipv6Only, }; + + #[allow(clippy::match_same_arms)] let multipath_strategy = match (multipath_strategy_cfg, addr_family) { (MultipathStrategyConfig::Classic, _) => Ok(MultipathStrategy::Classic), (MultipathStrategyConfig::Paris, _) => Ok(MultipathStrategy::Paris), - (MultipathStrategyConfig::Dublin, AddrFamily::Ipv4) => Ok(MultipathStrategy::Dublin), - (MultipathStrategyConfig::Dublin, AddrFamily::Ipv6) => Err(anyhow!( + ( + MultipathStrategyConfig::Dublin, + IpAddrFamily::Ipv4Only | IpAddrFamily::Ipv4thenIpv6 | IpAddrFamily::Ipv6thenIpv4, + ) => Ok(MultipathStrategy::Dublin), + (MultipathStrategyConfig::Dublin, IpAddrFamily::Ipv6Only) => Err(anyhow!( "Dublin multipath strategy not implemented for IPv6 yet!" )), }?; + let port_direction = match (protocol, source_port, target_port, multipath_strategy_cfg) { (Protocol::Icmp, _, _, _) => PortDirection::None, (Protocol::Udp, None, None, _) => PortDirection::new_fixed_src(pid.max(1024)), @@ -683,7 +718,7 @@ impl Default for TrippyConfig { Self { targets: vec![], protocol: defaults::DEFAULT_STRATEGY_PROTOCOL, - addr_family: defaults::DEFAULT_ADDRESS_FAMILY, + addr_family: dns_resolve_family(constants::DEFAULT_ADDR_FAMILY), first_ttl: defaults::DEFAULT_STRATEGY_FIRST_TTL, max_ttl: defaults::DEFAULT_STRATEGY_MAX_TTL, min_round_duration: defaults::DEFAULT_STRATEGY_MIN_ROUND_DURATION, @@ -743,6 +778,15 @@ fn dns_resolve_method(dns_resolve_method: DnsResolveMethodConfig) -> ResolveMeth } } +fn dns_resolve_family(dns_resolve_family: AddressFamilyConfig) -> IpAddrFamily { + match dns_resolve_family { + AddressFamilyConfig::Ipv4 => IpAddrFamily::Ipv4Only, + AddressFamilyConfig::Ipv6 => IpAddrFamily::Ipv6Only, + AddressFamilyConfig::Ipv6ThenIpv4 => IpAddrFamily::Ipv6thenIpv4, + AddressFamilyConfig::Ipv4ThenIpv6 => IpAddrFamily::Ipv4thenIpv6, + } +} + fn cfg_layer(fst: Option, snd: Option, def: T) -> T { match (fst, snd) { (Some(val), _) | (None, Some(val)) => val, @@ -1093,7 +1137,7 @@ mod tests { #[test_case("trip example.com", Ok(cfg().multipath_strategy(MultipathStrategy::Classic).build()); "default strategy")] #[test_case("trip example.com --multipath-strategy classic", Ok(cfg().multipath_strategy(MultipathStrategy::Classic).build()); "classic strategy")] #[test_case("trip example.com --multipath-strategy paris --udp", Ok(cfg().multipath_strategy(MultipathStrategy::Paris).protocol(Protocol::Udp).port_direction(PortDirection::FixedSrc(Port(1024))).build()); "paris strategy")] - #[test_case("trip example.com --multipath-strategy dublin --udp", Ok(cfg().multipath_strategy(MultipathStrategy::Dublin).protocol(Protocol::Udp).port_direction(PortDirection::FixedSrc(Port(1024))).build()); "dublin strategy")] + #[test_case("trip example.com --multipath-strategy dublin --udp", Ok(cfg().multipath_strategy(MultipathStrategy::Dublin).protocol(Protocol::Udp).addr_family(IpAddrFamily::Ipv4Only).port_direction(PortDirection::FixedSrc(Port(1024))).build()); "dublin strategy")] #[test_case("trip example.com --multipath-strategy tokyo", Err(anyhow!("error: one of the values isn't valid for an argument")); "invalid strategy")] #[test_case("trip example.com", Ok(cfg().protocol(Protocol::Icmp).port_direction(PortDirection::None).build()); "default protocol")] #[test_case("trip example.com --protocol icmp", Ok(cfg().protocol(Protocol::Icmp).port_direction(PortDirection::None).build()); "icmp protocol")] @@ -1116,18 +1160,24 @@ mod tests { #[test_case("trip example.com --udp --multipath-strategy paris --source-port 33000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Paris).port_direction(PortDirection::FixedSrc(Port(33000))).build()); "udp protocol paris strategy custom src port")] #[test_case("trip example.com --udp --multipath-strategy paris --target-port 5000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Paris).port_direction(PortDirection::FixedDest(Port(5000))).build()); "udp protocol paris strategy custom target port")] #[test_case("trip example.com --udp --multipath-strategy paris --source-port 33000 --target-port 5000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Paris).port_direction(PortDirection::FixedBoth(Port(33000), Port(5000))).build()); "udp protocol paris strategy custom both ports")] - #[test_case("trip example.com --udp --multipath-strategy dublin", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).port_direction(PortDirection::FixedSrc(Port(1024))).build()); "udp protocol dublin strategy default ports")] - #[test_case("trip example.com --udp --multipath-strategy dublin --source-port 33000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).port_direction(PortDirection::FixedSrc(Port(33000))).build()); "udp protocol dublin strategy custom src port")] - #[test_case("trip example.com --udp --multipath-strategy dublin --target-port 5000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).port_direction(PortDirection::FixedDest(Port(5000))).build()); "udp protocol dublin strategy custom target port")] - #[test_case("trip example.com --udp --multipath-strategy dublin --source-port 33000 --target-port 5000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).port_direction(PortDirection::FixedBoth(Port(33000), Port(5000))).build()); "udp protocol dublin strategy custom both ports")] + #[test_case("trip example.com --udp --multipath-strategy dublin", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).addr_family(IpAddrFamily::Ipv4Only).port_direction(PortDirection::FixedSrc(Port(1024))).build()); "udp protocol dublin strategy default ports")] + #[test_case("trip example.com --udp --multipath-strategy dublin --source-port 33000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).addr_family(IpAddrFamily::Ipv4Only).port_direction(PortDirection::FixedSrc(Port(33000))).build()); "udp protocol dublin strategy custom src port")] + #[test_case("trip example.com --udp --multipath-strategy dublin --target-port 5000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).addr_family(IpAddrFamily::Ipv4Only).port_direction(PortDirection::FixedDest(Port(5000))).build()); "udp protocol dublin strategy custom target port")] + #[test_case("trip example.com --udp --multipath-strategy dublin --source-port 33000 --target-port 5000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).addr_family(IpAddrFamily::Ipv4Only).port_direction(PortDirection::FixedBoth(Port(33000), Port(5000))).build()); "udp protocol dublin strategy custom both ports")] #[test_case("trip example.com --icmp --multipath-strategy paris", Err(anyhow!("Paris multipath strategy not support for icmp")); "paris with invalid protocol icmp")] #[test_case("trip example.com --icmp --multipath-strategy dublin", Err(anyhow!("Dublin multipath strategy not support for icmp")); "dublin with invalid protocol icmp")] #[test_case("trip example.com --tcp --multipath-strategy paris", Err(anyhow!("Paris multipath strategy not yet supported for tcp")); "paris with invalid protocol tcp")] #[test_case("trip example.com --tcp --multipath-strategy dublin", Err(anyhow!("Dublin multipath strategy not yet supported for tcp")); "dublin with invalid protocol tcp")] #[test_case("trip example.com --udp --source-port 33000 --target-port 5000", Err(anyhow!("only one of source-port and target-port may be fixed (except IPv4/udp protocol with dublin or paris strategy)")); "udp protocol custom both ports with invalid strategy")] - #[test_case("trip example.com", Ok(cfg().addr_family(AddrFamily::Ipv4).build()); "default address family shortcut")] - #[test_case("trip example.com -4", Ok(cfg().addr_family(AddrFamily::Ipv4).build()); "ipv4 address family shortcut")] - #[test_case("trip example.com -6", Ok(cfg().addr_family(AddrFamily::Ipv6).build()); "ipv6 address family shortcut")] + #[test_case("trip example.com", Ok(cfg().addr_family(IpAddrFamily::Ipv4thenIpv6).build()); "default address family")] + #[test_case("trip example.com --addr-family ipv4", Ok(cfg().addr_family(IpAddrFamily::Ipv4Only).build()); "ipv4 address family")] + #[test_case("trip example.com --addr-family ipv6", Ok(cfg().addr_family(IpAddrFamily::Ipv6Only).build()); "ipv6 address family")] + #[test_case("trip example.com --addr-family ipv4-then-ipv6", Ok(cfg().addr_family(IpAddrFamily::Ipv4thenIpv6).build()); "ipv4 then ipv6 address family")] + #[test_case("trip example.com --addr-family ipv6-then-ipv4", Ok(cfg().addr_family(IpAddrFamily::Ipv6thenIpv4).build()); "ipv6 then ipv4 address family")] + #[test_case("trip example.com -F ipv4", Ok(cfg().addr_family(IpAddrFamily::Ipv4Only).build()); "custom address family short")] + #[test_case("trip example.com --addr-family foo", Err(anyhow!("error: one of the values isn't valid for an argument")); "invalid address family")] + #[test_case("trip example.com -4", Ok(cfg().addr_family(IpAddrFamily::Ipv4Only).build()); "ipv4 address family shortcut")] + #[test_case("trip example.com -6", Ok(cfg().addr_family(IpAddrFamily::Ipv6Only).build()); "ipv6 address family shortcut")] #[test_case("trip example.com -5", Err(anyhow!("error: unexpected argument found")); "invalid address family shortcut")] #[test_case("trip example.com", Ok(cfg().first_ttl(1).build()); "default first ttl")] #[test_case("trip example.com --first-ttl 5", Ok(cfg().first_ttl(5).build()); "custom first ttl")] @@ -1316,7 +1366,7 @@ mod tests { } } - pub fn addr_family(self, addr_family: AddrFamily) -> Self { + pub fn addr_family(self, addr_family: IpAddrFamily) -> Self { Self { config: TrippyConfig { addr_family, diff --git a/src/config/cmd.rs b/src/config/cmd.rs index 2e58e96c..6299c06d 100644 --- a/src/config/cmd.rs +++ b/src/config/cmd.rs @@ -1,8 +1,9 @@ use crate::config::binding::TuiCommandItem; use crate::config::theme::TuiThemeItem; use crate::config::{ - AddressMode, AsMode, DnsResolveMethodConfig, GeoIpMode, IcmpExtensionMode, LogFormat, - LogSpanEvents, Mode, MultipathStrategyConfig, ProtocolConfig, TuiColor, TuiKeyBinding, + AddressFamilyConfig, AddressMode, AsMode, DnsResolveMethodConfig, GeoIpMode, IcmpExtensionMode, + LogFormat, LogSpanEvents, Mode, MultipathStrategyConfig, ProtocolConfig, TuiColor, + TuiKeyBinding, }; use anyhow::anyhow; use clap::builder::Styles; @@ -60,12 +61,26 @@ pub struct Args { )] pub icmp: bool, + /// The address family [default: Ipv4thenIpv6] + #[arg(value_enum, short = 'F', long)] + pub addr_family: Option, + /// Use IPv4 only - #[arg(short = '4', long, conflicts_with = "ipv6")] + #[arg( + short = '4', + long, + conflicts_with = "ipv6", + conflicts_with = "addr_family" + )] pub ipv4: bool, /// Use IPv6 only - #[arg(short = '6', long, conflicts_with = "ipv4")] + #[arg( + short = '6', + long, + conflicts_with = "ipv4", + conflicts_with = "addr_family" + )] pub ipv6: bool, /// The target port (TCP & UDP only) [default: 80] diff --git a/src/config/constants.rs b/src/config/constants.rs index 32a2211b..babec360 100644 --- a/src/config/constants.rs +++ b/src/config/constants.rs @@ -1,6 +1,6 @@ use crate::config::{ - AddressMode, AsMode, DnsResolveMethodConfig, GeoIpMode, IcmpExtensionMode, LogFormat, - LogSpanEvents, Mode, + AddressFamilyConfig, AddressMode, AsMode, DnsResolveMethodConfig, GeoIpMode, IcmpExtensionMode, + LogFormat, LogSpanEvents, Mode, }; use std::time::Duration; @@ -61,6 +61,9 @@ pub const DEFAULT_TUI_PRIVACY_MAX_TTL: u8 = 0; /// The default value for `dns-resolve-method`. pub const DEFAULT_DNS_RESOLVE_METHOD: DnsResolveMethodConfig = DnsResolveMethodConfig::System; +/// The default value for `addr-family`. +pub const DEFAULT_ADDR_FAMILY: AddressFamilyConfig = AddressFamilyConfig::Ipv4ThenIpv6; + /// The default value for `dns-lookup-as-info`. pub const DEFAULT_DNS_LOOKUP_AS_INFO: bool = false; diff --git a/src/config/file.rs b/src/config/file.rs index 4819ac9c..5b457b76 100644 --- a/src/config/file.rs +++ b/src/config/file.rs @@ -147,7 +147,7 @@ impl Default for ConfigStrategy { fn default() -> Self { Self { protocol: Some(ProtocolConfig::from(defaults::DEFAULT_STRATEGY_PROTOCOL)), - addr_family: Some(AddressFamilyConfig::from(defaults::DEFAULT_ADDRESS_FAMILY)), + addr_family: Some(super::constants::DEFAULT_ADDR_FAMILY), target_port: None, source_port: None, source_address: None, diff --git a/src/dns.rs b/src/dns.rs index 4aa29d5a..8c59773e 100644 --- a/src/dns.rs +++ b/src/dns.rs @@ -7,5 +7,5 @@ mod lazy_resolver; mod resolver; -pub use lazy_resolver::{Config, DnsResolver, ResolveMethod}; +pub use lazy_resolver::{Config, DnsResolver, IpAddrFamily, ResolveMethod}; pub use resolver::{AsInfo, DnsEntry, Error, Resolved, Resolver, Result, Unresolved}; diff --git a/src/dns/lazy_resolver.rs b/src/dns/lazy_resolver.rs index 6d6b569f..366c3652 100644 --- a/src/dns/lazy_resolver.rs +++ b/src/dns/lazy_resolver.rs @@ -1,4 +1,5 @@ use crate::dns::resolver::{DnsEntry, ResolvedIpAddrs, Resolver, Result}; +use std::fmt::{Display, Formatter}; use std::net::IpAddr; use std::rc::Rc; use std::time::Duration; @@ -8,7 +9,7 @@ use std::time::Duration; pub struct Config { /// The method to use for DNS resolution. pub resolve_method: ResolveMethod, - /// The address family to lookup. + /// The IP address resolution family. pub addr_family: IpAddrFamily, /// The timeout for DNS resolution. pub timeout: Duration, @@ -27,32 +28,41 @@ pub enum ResolveMethod { Cloudflare, } -/// The address family. -#[derive(Debug, Copy, Clone)] +/// How to resolve IP addresses. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum IpAddrFamily { - /// Internet Protocol v4. - Ipv4, - /// Internet Protocol v6. - Ipv6, + /// Lookup Ipv4 only. + Ipv4Only, + /// Lookup Ipv6 only. + Ipv6Only, + /// Lookup Ipv6 with a fallback to Ipv4 + Ipv6thenIpv4, + /// Lookup Ipv4 with a fallback to Ipv6 + Ipv4thenIpv6, } -impl Config { - /// Create an IPv4 `Config`. - #[must_use] - pub fn new_ipv4(resolve_method: ResolveMethod, timeout: Duration) -> Self { - Self { - resolve_method, - addr_family: IpAddrFamily::Ipv4, - timeout, +impl Display for IpAddrFamily { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::Ipv4Only => write!(f, "Ipv4Only"), + Self::Ipv6Only => write!(f, "Ipv6Only"), + Self::Ipv6thenIpv4 => write!(f, "Ipv6thenIpv4"), + Self::Ipv4thenIpv6 => write!(f, "Ipv4thenIpv6"), } } +} - /// Create an IPv6 `Config`. +impl Config { + /// Create a `Config`. #[must_use] - pub fn new_ipv6(resolve_method: ResolveMethod, timeout: Duration) -> Self { + pub fn new( + resolve_method: ResolveMethod, + addr_family: IpAddrFamily, + timeout: Duration, + ) -> Self { Self { resolve_method, - addr_family: IpAddrFamily::Ipv6, + addr_family, timeout, } } @@ -118,7 +128,7 @@ mod inner { use hickory_resolver::error::ResolveErrorKind; use hickory_resolver::proto::rr::RecordType; use hickory_resolver::{Name, Resolver}; - use itertools::Itertools; + use itertools::{Either, Itertools}; use parking_lot::RwLock; use std::collections::HashMap; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; @@ -168,8 +178,10 @@ mod inner { let mut options = ResolverOpts::default(); options.timeout = config.timeout; options.ip_strategy = match config.addr_family { - IpAddrFamily::Ipv4 => LookupIpStrategy::Ipv4Only, - IpAddrFamily::Ipv6 => LookupIpStrategy::Ipv6Only, + IpAddrFamily::Ipv4Only => LookupIpStrategy::Ipv4Only, + IpAddrFamily::Ipv6Only => LookupIpStrategy::Ipv6Only, + IpAddrFamily::Ipv6thenIpv4 => LookupIpStrategy::Ipv6thenIpv4, + IpAddrFamily::Ipv4thenIpv6 => LookupIpStrategy::Ipv4thenIpv6, }; let res = match config.resolve_method { ResolveMethod::Resolv => Resolver::from_system_conf(), @@ -208,17 +220,45 @@ mod inner { .map_err(|err| Error::LookupFailed(Box::new(err)))? .iter() .collect::>()), - DnsProvider::DnsLookup => Ok(dns_lookup::lookup_host(hostname) - .map_err(|err| Error::LookupFailed(Box::new(err)))? - .into_iter() - .filter(|addr| { - matches!( - (self.config.addr_family, addr), - (IpAddrFamily::Ipv4, IpAddr::V4(_)) - | (IpAddrFamily::Ipv6, IpAddr::V6(_)) - ) + DnsProvider::DnsLookup => { + let (ipv4, ipv6): (Vec<_>, Vec<_>) = dns_lookup::lookup_host(hostname) + .map_err(|err| Error::LookupFailed(Box::new(err)))? + .into_iter() + .partition_map(|ip| match ip { + IpAddr::V4(_) => Either::Left(ip), + IpAddr::V6(_) => Either::Right(ip), + }); + Ok(match self.config.addr_family { + IpAddrFamily::Ipv4Only => { + if ipv4.is_empty() { + vec![] + } else { + ipv4 + } + } + IpAddrFamily::Ipv6Only => { + if ipv6.is_empty() { + vec![] + } else { + ipv6 + } + } + IpAddrFamily::Ipv6thenIpv4 => { + if ipv6.is_empty() { + ipv4 + } else { + ipv6 + } + } + IpAddrFamily::Ipv4thenIpv6 => { + if ipv4.is_empty() { + ipv6 + } else { + ipv4 + } + } }) - .collect::>()), + } } .map(ResolvedIpAddrs) } diff --git a/src/frontend/render/header.rs b/src/frontend/render/header.rs index 680cd98a..0ca075c9 100644 --- a/src/frontend/render/header.rs +++ b/src/frontend/render/header.rs @@ -6,6 +6,7 @@ use ratatui::style::{Modifier, Style}; use ratatui::text::{Line, Span}; use ratatui::widgets::{Block, BorderType, Borders, Paragraph}; use ratatui::Frame; +use std::net::IpAddr; use std::time::Duration; use trippy::dns::{ResolveMethod, Resolver}; use trippy::tracing::{PortDirection, Protocol}; @@ -42,18 +43,18 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) { let protocol = match app.tracer_config().protocol { Protocol::Icmp => format!( "icmp({}, {})", - app.tracer_config().addr_family, + render_target_family(app.tracer_config().target_addr), app.tracer_config().privilege_mode ), Protocol::Udp => format!( "udp({}, {}, {})", - app.tracer_config().addr_family, + render_target_family(app.tracer_config().target_addr), app.tracer_config().multipath_strategy, app.tracer_config().privilege_mode ), Protocol::Tcp => format!( "tcp({}, {})", - app.tracer_config().addr_family, + render_target_family(app.tracer_config().target_addr), app.tracer_config().privilege_mode ), }; @@ -120,6 +121,13 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) { f.render_widget(left, rect); } +fn render_target_family(target: IpAddr) -> &'static str { + match target { + IpAddr::V4(_) => "v4", + IpAddr::V6(_) => "v6", + } +} + /// Render the source address of the trace. fn render_source(app: &TuiApp) -> String { let src_hostname = app diff --git a/src/main.rs b/src/main.rs index b00fb989..295b7431 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,8 +39,7 @@ use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; use trippy::dns::{DnsResolver, Resolver}; use trippy::tracing::{ - AddrFamily, ChannelConfig, Config, IcmpExtensionParseMode, MultipathStrategy, PortDirection, - Protocol, + ChannelConfig, Config, IcmpExtensionParseMode, MultipathStrategy, PortDirection, Protocol, }; use trippy::tracing::{PrivilegeMode, SourceAddr}; @@ -72,9 +71,9 @@ fn run_trippy(cfg: &TrippyConfig, platform: &Platform) -> anyhow::Result<()> { let addrs = resolve_targets(cfg, &resolver)?; if addrs.is_empty() { return Err(anyhow!( - "failed to find any valid IP{} addresses for {}", + "failed to find any valid IP addresses for {} for address family {}", + cfg.targets.join(", "), cfg.addr_family, - cfg.targets.join(", ") )); } let traces = start_tracers(cfg, &addrs, platform.pid)?; @@ -104,16 +103,11 @@ fn print_shell_completions(shell: Shell) -> anyhow::Result<()> { /// Start the DNS resolver. fn start_dns_resolver(cfg: &TrippyConfig) -> anyhow::Result { - Ok(match cfg.addr_family { - AddrFamily::Ipv4 => DnsResolver::start(trippy::dns::Config::new_ipv4( - cfg.dns_resolve_method, - cfg.dns_timeout, - ))?, - AddrFamily::Ipv6 => DnsResolver::start(trippy::dns::Config::new_ipv6( - cfg.dns_resolve_method, - cfg.dns_timeout, - ))?, - }) + Ok(DnsResolver::start(trippy::dns::Config::new( + cfg.dns_resolve_method, + cfg.addr_family, + cfg.dns_timeout, + ))?) } fn create_geoip_lookup(cfg: &TrippyConfig) -> anyhow::Result { @@ -316,7 +310,6 @@ fn make_trace_info( args.multipath_strategy, args.port_direction, args.protocol, - args.addr_family, args.first_ttl, args.max_ttl, args.grace_duration, @@ -372,7 +365,6 @@ pub struct TraceInfo { pub multipath_strategy: MultipathStrategy, pub port_direction: PortDirection, pub protocol: Protocol, - pub addr_family: AddrFamily, pub first_ttl: u8, pub max_ttl: u8, pub grace_duration: Duration, @@ -401,7 +393,6 @@ impl TraceInfo { multipath_strategy: MultipathStrategy, port_direction: PortDirection, protocol: Protocol, - addr_family: AddrFamily, first_ttl: u8, max_ttl: u8, grace_duration: Duration, @@ -426,7 +417,6 @@ impl TraceInfo { multipath_strategy, port_direction, protocol, - addr_family, first_ttl, max_ttl, grace_duration, diff --git a/src/tracing.rs b/src/tracing.rs index bfcdff28..3ef6b745 100644 --- a/src/tracing.rs +++ b/src/tracing.rs @@ -12,8 +12,8 @@ pub mod packet; pub use builder::Builder; pub use config::{ - defaults, AddrFamily, ChannelConfig, ChannelConfigBuilder, Config, ConfigBuilder, - IcmpExtensionParseMode, MultipathStrategy, PortDirection, PrivilegeMode, Protocol, + defaults, ChannelConfig, ChannelConfigBuilder, Config, ConfigBuilder, IcmpExtensionParseMode, + MultipathStrategy, PortDirection, PrivilegeMode, Protocol, }; pub use net::channel::TracerChannel; pub use net::source::SourceAddr; diff --git a/src/tracing/config.rs b/src/tracing/config.rs index 1c4ebe67..8c69e74c 100644 --- a/src/tracing/config.rs +++ b/src/tracing/config.rs @@ -11,7 +11,7 @@ use std::time::Duration; pub mod defaults { use crate::tracing::config::IcmpExtensionParseMode; - use crate::tracing::{AddrFamily, MultipathStrategy, PrivilegeMode, Protocol}; + use crate::tracing::{MultipathStrategy, PrivilegeMode, Protocol}; use std::time::Duration; /// The default value for `unprivileged`. @@ -20,9 +20,6 @@ pub mod defaults { /// The default value for `protocol`. pub const DEFAULT_STRATEGY_PROTOCOL: Protocol = Protocol::Icmp; - /// The default value for `addr-family`. - pub const DEFAULT_ADDRESS_FAMILY: AddrFamily = AddrFamily::Ipv4; - /// The default value for `multipath-strategy`. pub const DEFAULT_STRATEGY_MULTIPATH: MultipathStrategy = MultipathStrategy::Classic; @@ -123,24 +120,6 @@ impl Display for IcmpExtensionParseMode { } } -/// The address family. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum AddrFamily { - /// Internet Protocol V4 - Ipv4, - /// Internet Protocol V6 - Ipv6, -} - -impl Display for AddrFamily { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Self::Ipv4 => write!(f, "v4"), - Self::Ipv6 => write!(f, "v6"), - } - } -} - /// The tracing protocol. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Protocol { diff --git a/test_resources/completions_bash.txt b/test_resources/completions_bash.txt index 29f21927..7ca4b7cc 100644 --- a/test_resources/completions_bash.txt +++ b/test_resources/completions_bash.txt @@ -19,7 +19,7 @@ _trip() { case "${cmd}" in trip) - opts="-c -m -u -p -4 -6 -P -S -A -I -i -T -g -R -U -f -t -Q -e -r -y -z -a -M -s -C -G -v -h -V --config-file --mode --unprivileged --protocol --udp --tcp --icmp --ipv4 --ipv6 --target-port --source-port --source-address --interface --min-round-duration --max-round-duration --grace-duration --initial-sequence --multipath-strategy --max-inflight --first-ttl --max-ttl --packet-size --payload-pattern --tos --icmp-extensions --read-timeout --dns-resolve-method --dns-resolve-all --dns-timeout --dns-lookup-as-info --tui-address-mode --tui-as-mode --tui-custom-columns --tui-icmp-extension-mode --tui-geoip-mode --tui-max-addrs --tui-max-samples --tui-max-flows --tui-preserve-screen --tui-refresh-rate --tui-privacy-max-ttl --tui-theme-colors --print-tui-theme-items --tui-key-bindings --print-tui-binding-commands --report-cycles --geoip-mmdb-file --generate --print-config-template --log-format --log-filter --log-span-events --verbose --help --version [TARGETS]..." + opts="-c -m -u -p -F -4 -6 -P -S -A -I -i -T -g -R -U -f -t -Q -e -r -y -z -a -M -s -C -G -v -h -V --config-file --mode --unprivileged --protocol --udp --tcp --icmp --addr-family --ipv4 --ipv6 --target-port --source-port --source-address --interface --min-round-duration --max-round-duration --grace-duration --initial-sequence --multipath-strategy --max-inflight --first-ttl --max-ttl --packet-size --payload-pattern --tos --icmp-extensions --read-timeout --dns-resolve-method --dns-resolve-all --dns-timeout --dns-lookup-as-info --tui-address-mode --tui-as-mode --tui-custom-columns --tui-icmp-extension-mode --tui-geoip-mode --tui-max-addrs --tui-max-samples --tui-max-flows --tui-preserve-screen --tui-refresh-rate --tui-privacy-max-ttl --tui-theme-colors --print-tui-theme-items --tui-key-bindings --print-tui-binding-commands --report-cycles --geoip-mmdb-file --generate --print-config-template --log-format --log-filter --log-span-events --verbose --help --version [TARGETS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -49,6 +49,14 @@ _trip() { COMPREPLY=($(compgen -W "icmp udp tcp" -- "${cur}")) return 0 ;; + --addr-family) + COMPREPLY=($(compgen -W "ipv4 ipv6 ipv6-then-ipv4 ipv4-then-ipv6" -- "${cur}")) + return 0 + ;; + -F) + COMPREPLY=($(compgen -W "ipv4 ipv6 ipv6-then-ipv4 ipv4-then-ipv6" -- "${cur}")) + return 0 + ;; --target-port) COMPREPLY=($(compgen -f "${cur}")) return 0 @@ -280,3 +288,4 @@ if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERS else complete -F _trip -o bashdefault -o default trip fi + diff --git a/test_resources/completions_elvish.txt b/test_resources/completions_elvish.txt index e9aa5823..c4c30752 100644 --- a/test_resources/completions_elvish.txt +++ b/test_resources/completions_elvish.txt @@ -24,6 +24,8 @@ set edit:completion:arg-completer[trip] = {|@words| cand --mode 'Output mode [default: tui]' cand -p 'Tracing protocol [default: icmp]' cand --protocol 'Tracing protocol [default: icmp]' + cand -F 'The address family [default: Ipv4thenIpv6]' + cand --addr-family 'The address family [default: Ipv4thenIpv6]' cand -P 'The target port (TCP & UDP only) [default: 80]' cand --target-port 'The target port (TCP & UDP only) [default: 80]' cand -S 'The source port (TCP & UDP only) [default: auto]' @@ -107,3 +109,4 @@ set edit:completion:arg-completer[trip] = {|@words| ] $completions[$command] } + diff --git a/test_resources/completions_fish.txt b/test_resources/completions_fish.txt index d8da0833..aafd8220 100644 --- a/test_resources/completions_fish.txt +++ b/test_resources/completions_fish.txt @@ -1,6 +1,7 @@ complete -c trip -s c -l config-file -d 'Config file' -r -F -complete -c trip -s m -l mode -d 'Output mode [default: tui]' -r -f -a "{tui 'Display interactive TUI',stream 'Display a continuous stream of tracing data',pretty 'Generate an pretty text table report for N cycles',markdown 'Generate a markdown text table report for N cycles',csv 'Generate a CSV report for N cycles',json 'Generate a JSON report for N cycles',dot 'Generate a Graphviz DOT file for N cycles',flows 'Display all flows',silent 'Do not generate any tracing output for N cycles'}" -complete -c trip -s p -l protocol -d 'Tracing protocol [default: icmp]' -r -f -a "{icmp 'Internet Control Message Protocol',udp 'User Datagram Protocol',tcp 'Transmission Control Protocol'}" +complete -c trip -s m -l mode -d 'Output mode [default: tui]' -r -f -a "{tui 'Display interactive TUI',stream 'Display a continuous stream of tracing data',pretty 'Generate an pretty text table report for N cycles',markdown 'Generate a markdown text table report for N cycles',csv 'Generate a CSV report for N cycles',json 'Generate a JSON report for N cycles',dot 'Generate a Graphviz DOT file for N cycles',flows 'Display all flows',silent 'Do not generate any tracing output for N cycles'}" +complete -c trip -s p -l protocol -d 'Tracing protocol [default: icmp]' -r -f -a "{icmp 'Internet Control Message Protocol',udp 'User Datagram Protocol',tcp 'Transmission Control Protocol'}" +complete -c trip -s F -l addr-family -d 'The address family [default: Ipv4thenIpv6]' -r -f -a "{ipv4 'Ipv4 only',ipv6 'Ipv6 only',ipv6-then-ipv4 'Ipv6 with a fallback to Ipv4',ipv4-then-ipv6 'Ipv4 with a fallback to Ipv6'}" complete -c trip -s P -l target-port -d 'The target port (TCP & UDP only) [default: 80]' -r complete -c trip -s S -l source-port -d 'The source port (TCP & UDP only) [default: auto]' -r complete -c trip -s A -l source-address -d 'The source IP address [default: auto]' -r @@ -9,7 +10,7 @@ complete -c trip -s i -l min-round-duration -d 'The minimum duration of every ro complete -c trip -s T -l max-round-duration -d 'The maximum duration of every round [default: 1s]' -r complete -c trip -s g -l grace-duration -d 'The period of time to wait for additional ICMP responses after the target has responded [default: 100ms]' -r complete -c trip -l initial-sequence -d 'The initial sequence number [default: 33000]' -r -complete -c trip -s R -l multipath-strategy -d 'The Equal-cost Multi-Path routing strategy (UDP only) [default: classic]' -r -f -a "{classic 'The src or dest port is used to store the sequence number',paris 'The UDP `checksum` field is used to store the sequence number',dublin 'The IP `identifier` field is used to store the sequence number'}" +complete -c trip -s R -l multipath-strategy -d 'The Equal-cost Multi-Path routing strategy (UDP only) [default: classic]' -r -f -a "{classic 'The src or dest port is used to store the sequence number',paris 'The UDP `checksum` field is used to store the sequence number',dublin 'The IP `identifier` field is used to store the sequence number'}" complete -c trip -s U -l max-inflight -d 'The maximum number of in-flight ICMP echo requests [default: 24]' -r complete -c trip -s f -l first-ttl -d 'The TTL to start from [default: 1]' -r complete -c trip -s t -l max-ttl -d 'The maximum number of TTL hops [default: 64]' -r @@ -17,13 +18,13 @@ complete -c trip -l packet-size -d 'The size of IP packet to send (IP header + I complete -c trip -l payload-pattern -d 'The repeating pattern in the payload of the ICMP packet [default: 0]' -r complete -c trip -s Q -l tos -d 'The TOS (i.e. DSCP+ECN) IP header value (TCP and UDP only) [default: 0]' -r complete -c trip -l read-timeout -d 'The socket read timeout [default: 10ms]' -r -complete -c trip -s r -l dns-resolve-method -d 'How to perform DNS queries [default: system]' -r -f -a "{system 'Resolve using the OS resolver',resolv 'Resolve using the `/etc/resolv.conf` DNS configuration',google 'Resolve using the Google `8.8.8.8` DNS service',cloudflare 'Resolve using the Cloudflare `1.1.1.1` DNS service'}" +complete -c trip -s r -l dns-resolve-method -d 'How to perform DNS queries [default: system]' -r -f -a "{system 'Resolve using the OS resolver',resolv 'Resolve using the `/etc/resolv.conf` DNS configuration',google 'Resolve using the Google `8.8.8.8` DNS service',cloudflare 'Resolve using the Cloudflare `1.1.1.1` DNS service'}" complete -c trip -l dns-timeout -d 'The maximum time to wait to perform DNS queries [default: 5s]' -r -complete -c trip -s a -l tui-address-mode -d 'How to render addresses [default: host]' -r -f -a "{ip 'Show IP address only',host 'Show reverse-lookup DNS hostname only',both 'Show both IP address and reverse-lookup DNS hostname'}" -complete -c trip -l tui-as-mode -d 'How to render AS information [default: asn]' -r -f -a "{asn 'Show the ASN',prefix 'Display the AS prefix',country-code 'Display the country code',registry 'Display the registry name',allocated 'Display the allocated date',name 'Display the AS name'}" +complete -c trip -s a -l tui-address-mode -d 'How to render addresses [default: host]' -r -f -a "{ip 'Show IP address only',host 'Show reverse-lookup DNS hostname only',both 'Show both IP address and reverse-lookup DNS hostname'}" +complete -c trip -l tui-as-mode -d 'How to render AS information [default: asn]' -r -f -a "{asn 'Show the ASN',prefix 'Display the AS prefix',country-code 'Display the country code',registry 'Display the registry name',allocated 'Display the allocated date',name 'Display the AS name'}" complete -c trip -l tui-custom-columns -d 'Custom columns to be displayed in the TUI hops table [default: HOLSRAVBWDT]' -r -complete -c trip -l tui-icmp-extension-mode -d 'How to render ICMP extensions [default: off]' -r -f -a "{off 'Do not show `icmp` extensions',mpls 'Show MPLS label(s) only',full 'Show full `icmp` extension data for all known extensions',all 'Show full `icmp` extension data for all classes'}" -complete -c trip -l tui-geoip-mode -d 'How to render GeoIp information [default: short]' -r -f -a "{off 'Do not display GeoIp data',short 'Show short format',long 'Show long format',location 'Show latitude and Longitude format'}" +complete -c trip -l tui-icmp-extension-mode -d 'How to render ICMP extensions [default: off]' -r -f -a "{off 'Do not show `icmp` extensions',mpls 'Show MPLS label(s) only',full 'Show full `icmp` extension data for all known extensions',all 'Show full `icmp` extension data for all classes'}" +complete -c trip -l tui-geoip-mode -d 'How to render GeoIp information [default: short]' -r -f -a "{off 'Do not display GeoIp data',short 'Show short format',long 'Show long format',location 'Show latitude and Longitude format'}" complete -c trip -s M -l tui-max-addrs -d 'The maximum number of addresses to show per hop [default: auto]' -r complete -c trip -s s -l tui-max-samples -d 'The maximum number of samples to record per hop [default: 256]' -r complete -c trip -l tui-max-flows -d 'The maximum number of flows to show [default: 64]' -r @@ -33,10 +34,10 @@ complete -c trip -l tui-theme-colors -d 'The TUI theme colors [item=color,item=c complete -c trip -l tui-key-bindings -d 'The TUI key bindings [command=key,command=key,..]' -r complete -c trip -s C -l report-cycles -d 'The number of report cycles to run [default: 10]' -r complete -c trip -s G -l geoip-mmdb-file -d 'The MaxMind City GeoLite2 mmdb file' -r -F -complete -c trip -l generate -d 'Generate shell completion' -r -f -a "{bash '',elvish '',fish '',powershell '',zsh ''}" -complete -c trip -l log-format -d 'The debug log format [default: pretty]' -r -f -a "{compact 'Display log data in a compact format',pretty 'Display log data in a pretty format',json 'Display log data in a json format',chrome 'Display log data in Chrome trace format'}" +complete -c trip -l generate -d 'Generate shell completion' -r -f -a "{bash '',elvish '',fish '',powershell '',zsh ''}" +complete -c trip -l log-format -d 'The debug log format [default: pretty]' -r -f -a "{compact 'Display log data in a compact format',pretty 'Display log data in a pretty format',json 'Display log data in a json format',chrome 'Display log data in Chrome trace format'}" complete -c trip -l log-filter -d 'The debug log filter [default: trippy=debug]' -r -complete -c trip -l log-span-events -d 'The debug log format [default: off]' -r -f -a "{off 'Do not display event spans',active 'Display enter and exit event spans',full 'Display all event spans'}" +complete -c trip -l log-span-events -d 'The debug log format [default: off]' -r -f -a "{off 'Do not display event spans',active 'Display enter and exit event spans',full 'Display all event spans'}" complete -c trip -s u -l unprivileged -d 'Trace without requiring elevated privileges on supported platforms [default: false]' complete -c trip -l udp -d 'Trace using the UDP protocol' complete -c trip -l tcp -d 'Trace using the TCP protocol' @@ -52,4 +53,5 @@ complete -c trip -l print-tui-binding-commands -d 'Print all TUI commands that c complete -c trip -l print-config-template -d 'Print a template toml config file and exit' complete -c trip -s v -l verbose -d 'Enable verbose debug logging' complete -c trip -s h -l help -d 'Print help (see more with \'--help\')' -complete -c trip -s V -l version -d 'Print version' \ No newline at end of file +complete -c trip -s V -l version -d 'Print version' + diff --git a/test_resources/completions_powershell.txt b/test_resources/completions_powershell.txt index c5e65951..93157c04 100644 --- a/test_resources/completions_powershell.txt +++ b/test_resources/completions_powershell.txt @@ -27,6 +27,8 @@ Register-ArgumentCompleter -Native -CommandName 'trip' -ScriptBlock { [CompletionResult]::new('--mode', 'mode', [CompletionResultType]::ParameterName, 'Output mode [default: tui]') [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Tracing protocol [default: icmp]') [CompletionResult]::new('--protocol', 'protocol', [CompletionResultType]::ParameterName, 'Tracing protocol [default: icmp]') + [CompletionResult]::new('-F', 'F ', [CompletionResultType]::ParameterName, 'The address family [default: Ipv4thenIpv6]') + [CompletionResult]::new('--addr-family', 'addr-family', [CompletionResultType]::ParameterName, 'The address family [default: Ipv4thenIpv6]') [CompletionResult]::new('-P', 'P ', [CompletionResultType]::ParameterName, 'The target port (TCP & UDP only) [default: 80]') [CompletionResult]::new('--target-port', 'target-port', [CompletionResultType]::ParameterName, 'The target port (TCP & UDP only) [default: 80]') [CompletionResult]::new('-S', 'S ', [CompletionResultType]::ParameterName, 'The source port (TCP & UDP only) [default: auto]') @@ -113,3 +115,4 @@ Register-ArgumentCompleter -Native -CommandName 'trip' -ScriptBlock { $completions.Where{ $_.CompletionText -like "$wordToComplete*" } | Sort-Object -Property ListItemText } + diff --git a/test_resources/completions_zsh.txt b/test_resources/completions_zsh.txt index 3b977a85..a87aae18 100644 --- a/test_resources/completions_zsh.txt +++ b/test_resources/completions_zsh.txt @@ -41,6 +41,14 @@ tcp\:"Transmission Control Protocol"))' \ '--protocol=[Tracing protocol \[default\: icmp\]]:PROTOCOL:((icmp\:"Internet Control Message Protocol" udp\:"User Datagram Protocol" tcp\:"Transmission Control Protocol"))' \ +'-F+[The address family \[default\: Ipv4thenIpv6\]]:ADDR_FAMILY:((ipv4\:"Ipv4 only" +ipv6\:"Ipv6 only" +ipv6-then-ipv4\:"Ipv6 with a fallback to Ipv4" +ipv4-then-ipv6\:"Ipv4 with a fallback to Ipv6"))' \ +'--addr-family=[The address family \[default\: Ipv4thenIpv6\]]:ADDR_FAMILY:((ipv4\:"Ipv4 only" +ipv6\:"Ipv6 only" +ipv6-then-ipv4\:"Ipv6 with a fallback to Ipv4" +ipv4-then-ipv6\:"Ipv4 with a fallback to Ipv6"))' \ '-P+[The target port (TCP & UDP only) \[default\: 80\]]:TARGET_PORT: ' \ '--target-port=[The target port (TCP & UDP only) \[default\: 80\]]:TARGET_PORT: ' \ '-S+[The source port (TCP & UDP only) \[default\: auto\]]:SOURCE_PORT: ' \ @@ -130,10 +138,10 @@ full\:"Display all event spans"))' \ '(-p --protocol --tcp --icmp)--udp[Trace using the UDP protocol]' \ '(-p --protocol --udp --icmp)--tcp[Trace using the TCP protocol]' \ '(-p --protocol --udp --tcp)--icmp[Trace using the ICMP protocol]' \ -'(-6 --ipv6)-4[Use IPv4 only]' \ -'(-6 --ipv6)--ipv4[Use IPv4 only]' \ -'(-4 --ipv4)-6[Use IPv6 only]' \ -'(-4 --ipv4)--ipv6[Use IPv6 only]' \ +'(-6 --ipv6 -F --addr-family)-4[Use IPv4 only]' \ +'(-6 --ipv6 -F --addr-family)--ipv4[Use IPv4 only]' \ +'(-4 --ipv4 -F --addr-family)-6[Use IPv6 only]' \ +'(-4 --ipv4 -F --addr-family)--ipv6[Use IPv6 only]' \ '-e[Parse ICMP extensions]' \ '--icmp-extensions[Parse ICMP extensions]' \ '-y[Trace to all IPs resolved from DNS lookup \[default\: false\]]' \ @@ -165,3 +173,4 @@ if [ "$funcstack[1]" = "_trip" ]; then else compdef _trip trip fi + diff --git a/test_resources/usage_long.txt b/test_resources/usage_long.txt index d5cad83d..13a541e1 100644 --- a/test_resources/usage_long.txt +++ b/test_resources/usage_long.txt @@ -44,6 +44,15 @@ Options: --icmp Trace using the ICMP protocol + -F, --addr-family + The address family [default: Ipv4thenIpv6] + + Possible values: + - ipv4: Ipv4 only + - ipv6: Ipv6 only + - ipv6-then-ipv4: Ipv6 with a fallback to Ipv4 + - ipv4-then-ipv6: Ipv4 with a fallback to Ipv6 + -4, --ipv4 Use IPv4 only diff --git a/test_resources/usage_short.txt b/test_resources/usage_short.txt index f8844941..b2299d12 100644 --- a/test_resources/usage_short.txt +++ b/test_resources/usage_short.txt @@ -7,12 +7,15 @@ Arguments: Options: -c, --config-file Config file - -m, --mode Output mode [default: tui] [possible values: tui, stream, pretty, markdown, csv, json, dot, flows, silent] + -m, --mode Output mode [default: tui] [possible values: tui, stream, pretty, markdown, csv, json, dot, + flows, silent] -u, --unprivileged Trace without requiring elevated privileges on supported platforms [default: false] -p, --protocol Tracing protocol [default: icmp] [possible values: icmp, udp, tcp] --udp Trace using the UDP protocol --tcp Trace using the TCP protocol --icmp Trace using the ICMP protocol + -F, --addr-family The address family [default: Ipv4thenIpv6] [possible values: ipv4, ipv6, ipv6-then-ipv4, + ipv4-then-ipv6] -4, --ipv4 Use IPv4 only -6, --ipv6 Use IPv6 only -P, --target-port The target port (TCP & UDP only) [default: 80] @@ -21,9 +24,11 @@ Options: -I, --interface The network interface [default: auto] -i, --min-round-duration The minimum duration of every round [default: 1s] -T, --max-round-duration The maximum duration of every round [default: 1s] - -g, --grace-duration The period of time to wait for additional ICMP responses after the target has responded [default: 100ms] + -g, --grace-duration The period of time to wait for additional ICMP responses after the target has responded [default: + 100ms] --initial-sequence The initial sequence number [default: 33000] - -R, --multipath-strategy The Equal-cost Multi-Path routing strategy (UDP only) [default: classic] [possible values: classic, paris, dublin] + -R, --multipath-strategy The Equal-cost Multi-Path routing strategy (UDP only) [default: classic] [possible values: + classic, paris, dublin] -U, --max-inflight The maximum number of in-flight ICMP echo requests [default: 24] -f, --first-ttl The TTL to start from [default: 1] -t, --max-ttl The maximum number of TTL hops [default: 64] @@ -32,12 +37,14 @@ Options: -Q, --tos The TOS (i.e. DSCP+ECN) IP header value (TCP and UDP only) [default: 0] -e, --icmp-extensions Parse ICMP extensions --read-timeout The socket read timeout [default: 10ms] - -r, --dns-resolve-method How to perform DNS queries [default: system] [possible values: system, resolv, google, cloudflare] + -r, --dns-resolve-method How to perform DNS queries [default: system] [possible values: system, resolv, google, + cloudflare] -y, --dns-resolve-all Trace to all IPs resolved from DNS lookup [default: false] --dns-timeout The maximum time to wait to perform DNS queries [default: 5s] -z, --dns-lookup-as-info Lookup autonomous system (AS) information during DNS queries [default: false] -a, --tui-address-mode How to render addresses [default: host] [possible values: ip, host, both] - --tui-as-mode How to render AS information [default: asn] [possible values: asn, prefix, country-code, registry, allocated, name] + --tui-as-mode How to render AS information [default: asn] [possible values: asn, prefix, country-code, + registry, allocated, name] --tui-custom-columns Custom columns to be displayed in the TUI hops table [default: HOLSRAVBWDT] --tui-icmp-extension-mode How to render ICMP extensions [default: off] [possible values: off, mpls, full, all] --tui-geoip-mode How to render GeoIp information [default: short] [possible values: off, short, long, location] diff --git a/trippy-config-sample.toml b/trippy-config-sample.toml index 3f5abbd9..1bdfd475 100644 --- a/trippy-config-sample.toml +++ b/trippy-config-sample.toml @@ -78,9 +78,11 @@ protocol = "icmp" # The address family. # # Allowed values are: -# ipv4 [default] -# ipv6 -addr-family = "ipv4" +# ipv4 - Lookup Ipv4 only +# ipv6 - Lookup Ipv6 only +# ipv6-then-ipv4 - Lookup Ipv6 with a fallback to Ipv4 +# ipv4-then-ipv6 - Lookup Ipv4 with a fallback to Ipv6 [default] +addr-family = "ipv4-then-ipv6" # The target port (TCP & UDP only) [default: 80] #