Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce system address family #1470

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions crates/trippy-dns/src/lazy_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@ pub enum IpAddrFamily {
Ipv4Only,
/// Lookup IPv6 only.
Ipv6Only,
/// Lookup IPv6 with a fallback to IPv4
/// Lookup IPv6 with a fallback to IPv4.
Ipv6thenIpv4,
/// Lookup IPv4 with a fallback to IPv6
/// Lookup IPv4 with a fallback to IPv6.
Ipv4thenIpv6,
/// Use the first IP address returned by the OS resolver when using `ResolveMethod::System`,
/// otherwise lookup IPv6 with a fallback to IPv4.
System,
}

impl Display for IpAddrFamily {
Expand All @@ -37,6 +40,7 @@ impl Display for IpAddrFamily {
Self::Ipv6Only => write!(f, "Ipv6Only"),
Self::Ipv6thenIpv4 => write!(f, "Ipv6thenIpv4"),
Self::Ipv4thenIpv6 => write!(f, "Ipv4thenIpv6"),
Self::System => write!(f, "System"),
}
}
}
Expand Down Expand Up @@ -167,11 +171,14 @@ mod inner {
DnsProvider::DnsLookup
} else {
let mut options = ResolverOpts::default();
#[allow(clippy::match_same_arms)]
let ip_strategy = match config.addr_family {
IpAddrFamily::Ipv4Only => LookupIpStrategy::Ipv4Only,
IpAddrFamily::Ipv6Only => LookupIpStrategy::Ipv6Only,
IpAddrFamily::Ipv6thenIpv4 => LookupIpStrategy::Ipv6thenIpv4,
IpAddrFamily::Ipv4thenIpv6 => LookupIpStrategy::Ipv4thenIpv6,
// see issue #1469
IpAddrFamily::System => LookupIpStrategy::Ipv4thenIpv6,
};
options.timeout = config.timeout;
options.ip_strategy = ip_strategy;
Expand Down Expand Up @@ -211,49 +218,55 @@ mod inner {
}

pub(super) fn lookup(&self, hostname: &str) -> Result<ResolvedIpAddrs> {
fn partition(all: Vec<IpAddr>) -> (Vec<IpAddr>, Vec<IpAddr>) {
all.into_iter().partition_map(|ip| match ip {
IpAddr::V4(_) => Either::Left(ip),
IpAddr::V6(_) => Either::Right(ip),
})
}
match &self.provider {
DnsProvider::TrustDns(resolver) => Ok(resolver
.lookup_ip(hostname)
.map_err(|err| Error::LookupFailed(Box::new(err)))?
.iter()
.collect::<Vec<_>>()),
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),
});
let all = dns_lookup::lookup_host(hostname)
.map_err(|err| Error::LookupFailed(Box::new(err)))?;
Ok(match self.config.addr_family {
IpAddrFamily::Ipv4Only => {
let (ipv4, _) = partition(all);
if ipv4.is_empty() {
vec![]
} else {
ipv4
}
}
IpAddrFamily::Ipv6Only => {
let (_, ipv6) = partition(all);
if ipv6.is_empty() {
vec![]
} else {
ipv6
}
}
IpAddrFamily::Ipv6thenIpv4 => {
let (ipv4, ipv6) = partition(all);
if ipv6.is_empty() {
ipv4
} else {
ipv6
}
}
IpAddrFamily::Ipv4thenIpv6 => {
let (ipv4, ipv6) = partition(all);
if ipv4.is_empty() {
ipv6
} else {
ipv4
}
}
IpAddrFamily::System => all,
})
}
}
Expand Down
23 changes: 17 additions & 6 deletions crates/trippy-tui/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,15 @@ pub enum AddressFamilyConfig {
Ipv4,
/// IPv6 only.
Ipv6,
/// IPv6 with a fallback to IPv4
/// IPv6 with a fallback to IPv4.
#[serde(rename = "ipv6-then-ipv4")]
Ipv6ThenIpv4,
/// IPv4 with a fallback to IPv6
/// IPv4 with a fallback to IPv6.
#[serde(rename = "ipv4-then-ipv6")]
Ipv4ThenIpv6,
/// Use the first IP address returned by the OS resolver when using
/// `DnsResolveMethodConfig::System`, otherwise lookup IPv6 with a fallback to IPv4.
System,
}

impl From<IpAddrFamily> for AddressFamilyConfig {
Expand All @@ -96,6 +99,7 @@ impl From<IpAddrFamily> for AddressFamilyConfig {
IpAddrFamily::Ipv6Only => Self::Ipv6,
IpAddrFamily::Ipv6thenIpv4 => Self::Ipv6ThenIpv4,
IpAddrFamily::Ipv4thenIpv6 => Self::Ipv4ThenIpv6,
IpAddrFamily::System => Self::System,
}
}
}
Expand Down Expand Up @@ -582,8 +586,12 @@ impl TrippyConfig {
(false, false, AddressFamilyConfig::Ipv6ThenIpv4, MultipathStrategyConfig::Dublin) => {
IpAddrFamily::Ipv4Only
}
(false, false, AddressFamilyConfig::System, MultipathStrategyConfig::Dublin) => {
IpAddrFamily::Ipv4Only
}
(false, false, AddressFamilyConfig::Ipv4ThenIpv6, _) => IpAddrFamily::Ipv4thenIpv6,
(false, false, AddressFamilyConfig::Ipv6ThenIpv4, _) => IpAddrFamily::Ipv6thenIpv4,
(false, false, AddressFamilyConfig::System, _) => IpAddrFamily::System,
(true, _, _, _) => IpAddrFamily::Ipv4Only,
(_, true, _, _) => IpAddrFamily::Ipv6Only,
};
Expand Down Expand Up @@ -786,6 +794,7 @@ const fn dns_resolve_family(dns_resolve_family: AddressFamilyConfig) -> IpAddrFa
AddressFamilyConfig::Ipv6 => IpAddrFamily::Ipv6Only,
AddressFamilyConfig::Ipv6ThenIpv4 => IpAddrFamily::Ipv6thenIpv4,
AddressFamilyConfig::Ipv4ThenIpv6 => IpAddrFamily::Ipv4thenIpv6,
AddressFamilyConfig::System => IpAddrFamily::System,
}
}

Expand Down Expand Up @@ -1030,9 +1039,10 @@ fn validate_grace_duration(grace_duration: Duration) -> anyhow::Result<()> {
fn validate_packet_size(address_family: IpAddrFamily, packet_size: u16) -> anyhow::Result<()> {
let min_size = match address_family {
IpAddrFamily::Ipv4Only => constants::MIN_PACKET_SIZE_IPV4,
IpAddrFamily::Ipv6Only | IpAddrFamily::Ipv6thenIpv4 | IpAddrFamily::Ipv4thenIpv6 => {
constants::MIN_PACKET_SIZE_IPV6
}
IpAddrFamily::Ipv6Only
| IpAddrFamily::Ipv6thenIpv4
| IpAddrFamily::Ipv4thenIpv6
| IpAddrFamily::System => constants::MIN_PACKET_SIZE_IPV6,
};
if (min_size..=constants::MAX_PACKET_SIZE).contains(&packet_size) {
Ok(())
Expand Down Expand Up @@ -1267,8 +1277,9 @@ mod tests {
#[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 --addr-family system", Ok(cfg().addr_family(IpAddrFamily::System).build()); "system 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: invalid value 'foo' for '--addr-family <ADDR_FAMILY>' [possible values: ipv4, ipv6, ipv6-then-ipv4, ipv4-then-ipv6] For more information, try '--help'.")); "invalid address family")]
#[test_case("trip example.com --addr-family foo", Err(anyhow!("error: invalid value 'foo' for '--addr-family <ADDR_FAMILY>' [possible values: ipv4, ipv6, ipv6-then-ipv4, ipv4-then-ipv6, system] For more information, try '--help'.")); "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 '-5' found tip: to pass '-5' as a value, use '-- -5' Usage: trip [OPTIONS] [TARGETS]... For more information, try '--help'.")); "invalid address family shortcut")]
Expand Down
2 changes: 1 addition & 1 deletion ...rippy-tui/tests/resources/snapshots/[email protected]
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
---
source: crates/trippy-tui/src/config.rs
---
AnetworkdiagnostictoolUsage:trip[OPTIONS][TARGETS]...Arguments:[TARGETS]...AspacedelimitedlistofhostnamesandIPstotraceOptions:-c,--config-file<CONFIG_FILE>Configfile-m,--mode<MODE>Outputmode[default:tui][possiblevalues:tui,stream,pretty,markdown,csv,json,dot,flows,silent]-u,--unprivilegedTracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]-p,--protocol<PROTOCOL>Tracingprotocol[default:icmp][possiblevalues:icmp,udp,tcp]--udpTraceusingtheUDPprotocol--tcpTraceusingtheTCPprotocol--icmpTraceusingtheICMPprotocol-F,--addr-family<ADDR_FAMILY>Theaddressfamily[default:ipv4-then-ipv6][possiblevalues:ipv4,ipv6,ipv6-then-ipv4,ipv4-then-ipv6]-4,--ipv4UseIPv4only-6,--ipv6UseIPv6only-P,--target-port<TARGET_PORT>Thetargetport(TCP&UDPonly)[default:80]-S,--source-port<SOURCE_PORT>Thesourceport(TCP&UDPonly)[default:auto]-A,--source-address<SOURCE_ADDRESS>ThesourceIPaddress[default:auto]-I,--interface<INTERFACE>Thenetworkinterface[default:auto]-i,--min-round-duration<MIN_ROUND_DURATION>Theminimumdurationofeveryround[default:1s]-T,--max-round-duration<MAX_ROUND_DURATION>Themaximumdurationofeveryround[default:1s]-g,--grace-duration<GRACE_DURATION>TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]--initial-sequence<INITIAL_SEQUENCE>Theinitialsequencenumber[default:33434]-R,--multipath-strategy<MULTIPATH_STRATEGY>TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic][possiblevalues:classic,paris,dublin]-U,--max-inflight<MAX_INFLIGHT>Themaximumnumberofin-flightICMPechorequests[default:24]-f,--first-ttl<FIRST_TTL>TheTTLtostartfrom[default:1]-t,--max-ttl<MAX_TTL>ThemaximumnumberofTTLhops[default:64]--packet-size<PACKET_SIZE>ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]--payload-pattern<PAYLOAD_PATTERN>TherepeatingpatterninthepayloadoftheICMPpacket[default:0]-Q,--tos<TOS>TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]-e,--icmp-extensionsParseICMPextensions--read-timeout<READ_TIMEOUT>Thesocketreadtimeout[default:10ms]-r,--dns-resolve-method<DNS_RESOLVE_METHOD>HowtoperformDNSqueries[default:system][possiblevalues:system,resolv,google,cloudflare]-y,--dns-resolve-allTracetoallIPsresolvedfromDNSlookup[default:false]--dns-timeout<DNS_TIMEOUT>ThemaximumtimetowaittoperformDNSqueries[default:5s]--dns-ttl<DNS_TTL>Thetime-to-live(TTL)ofDNSentries[default:300s]-z,--dns-lookup-as-infoLookupautonomoussystem(AS)informationduringDNSqueries[default:false]-s,--max-samples<MAX_SAMPLES>Themaximumnumberofsamplestorecordperhop[default:256]--max-flows<MAX_FLOWS>Themaximumnumberofflowstorecord[default:64]-a,--tui-address-mode<TUI_ADDRESS_MODE>Howtorenderaddresses[default:host][possiblevalues:ip,host,both]--tui-as-mode<TUI_AS_MODE>Howtorenderautonomoussystem(AS)information[default:asn][possiblevalues:asn,prefix,country-code,registry,allocated,name]--tui-custom-columns<TUI_CUSTOM_COLUMNS>CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]--tui-icmp-extension-mode<TUI_ICMP_EXTENSION_MODE>HowtorenderICMPextensions[default:off][possiblevalues:off,mpls,full,all]--tui-geoip-mode<TUI_GEOIP_MODE>HowtorenderGeoIpinformation[default:short][possiblevalues:off,short,long,location]-M,--tui-max-addrs<TUI_MAX_ADDRS>Themaximumnumberofaddressestoshowperhop[default:auto]--tui-preserve-screenPreservethescreenonexit[default:false]--tui-refresh-rate<TUI_REFRESH_RATE>TheTUIrefreshrate[default:100ms]--tui-privacy-max-ttl<TUI_PRIVACY_MAX_TTL>Themaximumttlofhopswhichwillbemaskedforprivacy[default:none]--tui-locale<TUI_LOCALE>ThelocaletousefortheTUI[default:auto]--tui-theme-colors<TUI_THEME_COLORS>TheTUIthemecolors[item=color,item=color,..]--print-tui-theme-itemsPrintallTUIthemeitemsandexit--tui-key-bindings<TUI_KEY_BINDINGS>TheTUIkeybindings[command=key,command=key,..]--print-tui-binding-commandsPrintallTUIcommandsthatcanbeboundandexit-C,--report-cycles<REPORT_CYCLES>Thenumberofreportcyclestorun[default:10]-G,--geoip-mmdb-file<GEOIP_MMDB_FILE>ThesupportedMaxMindorIPinfoGeoIpmmdbfile--generate<GENERATE>Generateshellcompletion[possiblevalues:bash,elvish,fish,powershell,zsh]--generate-manGenerateROFFmanpage--print-config-templatePrintatemplatetomlconfigfileandexit--print-localesPrintallavailableTUIlocalesandexit--log-format<LOG_FORMAT>Thedebuglogformat[default:pretty][possiblevalues:compact,pretty,json,chrome]--log-filter<LOG_FILTER>Thedebuglogfilter[default:trippy=debug]--log-span-events<LOG_SPAN_EVENTS>Thedebuglogformat[default:off][possiblevalues:off,active,full]-v,--verboseEnableverbosedebuglogging-h,--helpPrinthelp(seemorewith'--help')-V,--versionPrintversion
AnetworkdiagnostictoolUsage:trip[OPTIONS][TARGETS]...Arguments:[TARGETS]...AspacedelimitedlistofhostnamesandIPstotraceOptions:-c,--config-file<CONFIG_FILE>Configfile-m,--mode<MODE>Outputmode[default:tui][possiblevalues:tui,stream,pretty,markdown,csv,json,dot,flows,silent]-u,--unprivilegedTracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]-p,--protocol<PROTOCOL>Tracingprotocol[default:icmp][possiblevalues:icmp,udp,tcp]--udpTraceusingtheUDPprotocol--tcpTraceusingtheTCPprotocol--icmpTraceusingtheICMPprotocol-F,--addr-family<ADDR_FAMILY>Theaddressfamily[default:ipv4-then-ipv6][possiblevalues:ipv4,ipv6,ipv6-then-ipv4,ipv4-then-ipv6,system]-4,--ipv4UseIPv4only-6,--ipv6UseIPv6only-P,--target-port<TARGET_PORT>Thetargetport(TCP&UDPonly)[default:80]-S,--source-port<SOURCE_PORT>Thesourceport(TCP&UDPonly)[default:auto]-A,--source-address<SOURCE_ADDRESS>ThesourceIPaddress[default:auto]-I,--interface<INTERFACE>Thenetworkinterface[default:auto]-i,--min-round-duration<MIN_ROUND_DURATION>Theminimumdurationofeveryround[default:1s]-T,--max-round-duration<MAX_ROUND_DURATION>Themaximumdurationofeveryround[default:1s]-g,--grace-duration<GRACE_DURATION>TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]--initial-sequence<INITIAL_SEQUENCE>Theinitialsequencenumber[default:33434]-R,--multipath-strategy<MULTIPATH_STRATEGY>TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic][possiblevalues:classic,paris,dublin]-U,--max-inflight<MAX_INFLIGHT>Themaximumnumberofin-flightICMPechorequests[default:24]-f,--first-ttl<FIRST_TTL>TheTTLtostartfrom[default:1]-t,--max-ttl<MAX_TTL>ThemaximumnumberofTTLhops[default:64]--packet-size<PACKET_SIZE>ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]--payload-pattern<PAYLOAD_PATTERN>TherepeatingpatterninthepayloadoftheICMPpacket[default:0]-Q,--tos<TOS>TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]-e,--icmp-extensionsParseICMPextensions--read-timeout<READ_TIMEOUT>Thesocketreadtimeout[default:10ms]-r,--dns-resolve-method<DNS_RESOLVE_METHOD>HowtoperformDNSqueries[default:system][possiblevalues:system,resolv,google,cloudflare]-y,--dns-resolve-allTracetoallIPsresolvedfromDNSlookup[default:false]--dns-timeout<DNS_TIMEOUT>ThemaximumtimetowaittoperformDNSqueries[default:5s]--dns-ttl<DNS_TTL>Thetime-to-live(TTL)ofDNSentries[default:300s]-z,--dns-lookup-as-infoLookupautonomoussystem(AS)informationduringDNSqueries[default:false]-s,--max-samples<MAX_SAMPLES>Themaximumnumberofsamplestorecordperhop[default:256]--max-flows<MAX_FLOWS>Themaximumnumberofflowstorecord[default:64]-a,--tui-address-mode<TUI_ADDRESS_MODE>Howtorenderaddresses[default:host][possiblevalues:ip,host,both]--tui-as-mode<TUI_AS_MODE>Howtorenderautonomoussystem(AS)information[default:asn][possiblevalues:asn,prefix,country-code,registry,allocated,name]--tui-custom-columns<TUI_CUSTOM_COLUMNS>CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]--tui-icmp-extension-mode<TUI_ICMP_EXTENSION_MODE>HowtorenderICMPextensions[default:off][possiblevalues:off,mpls,full,all]--tui-geoip-mode<TUI_GEOIP_MODE>HowtorenderGeoIpinformation[default:short][possiblevalues:off,short,long,location]-M,--tui-max-addrs<TUI_MAX_ADDRS>Themaximumnumberofaddressestoshowperhop[default:auto]--tui-preserve-screenPreservethescreenonexit[default:false]--tui-refresh-rate<TUI_REFRESH_RATE>TheTUIrefreshrate[default:100ms]--tui-privacy-max-ttl<TUI_PRIVACY_MAX_TTL>Themaximumttlofhopswhichwillbemaskedforprivacy[default:none]--tui-locale<TUI_LOCALE>ThelocaletousefortheTUI[default:auto]--tui-theme-colors<TUI_THEME_COLORS>TheTUIthemecolors[item=color,item=color,..]--print-tui-theme-itemsPrintallTUIthemeitemsandexit--tui-key-bindings<TUI_KEY_BINDINGS>TheTUIkeybindings[command=key,command=key,..]--print-tui-binding-commandsPrintallTUIcommandsthatcanbeboundandexit-C,--report-cycles<REPORT_CYCLES>Thenumberofreportcyclestorun[default:10]-G,--geoip-mmdb-file<GEOIP_MMDB_FILE>ThesupportedMaxMindorIPinfoGeoIpmmdbfile--generate<GENERATE>Generateshellcompletion[possiblevalues:bash,elvish,fish,powershell,zsh]--generate-manGenerateROFFmanpage--print-config-templatePrintatemplatetomlconfigfileandexit--print-localesPrintallavailableTUIlocalesandexit--log-format<LOG_FORMAT>Thedebuglogformat[default:pretty][possiblevalues:compact,pretty,json,chrome]--log-filter<LOG_FILTER>Thedebuglogfilter[default:trippy=debug]--log-span-events<LOG_SPAN_EVENTS>Thedebuglogformat[default:off][possiblevalues:off,active,full]-v,--verboseEnableverbosedebuglogging-h,--helpPrinthelp(seemorewith'--help')-V,--versionPrintversion
Loading