diff --git a/iroh-dns-server/src/store.rs b/iroh-dns-server/src/store.rs index f5e156ff40..8ee036f7f5 100644 --- a/iroh-dns-server/src/store.rs +++ b/iroh-dns-server/src/store.rs @@ -34,7 +34,7 @@ pub enum PacketSource { /// A store for pkarr signed packets. /// -/// Packets are stored in the persistent [`SignedPacketStore`], and cached on-demand in an in-memory LRU +/// Packets are stored in the persistent `SignedPacketStore`, and cached on-demand in an in-memory LRU /// cache used for resolving DNS queries. #[derive(Debug, Clone)] pub struct ZoneStore { diff --git a/iroh-net-report/src/lib.rs b/iroh-net-report/src/lib.rs index 4379590ac5..1f622247c7 100644 --- a/iroh-net-report/src/lib.rs +++ b/iroh-net-report/src/lib.rs @@ -10,7 +10,7 @@ #![cfg_attr(not(test), deny(clippy::unwrap_used))] use std::{ - collections::{BTreeMap, HashMap}, + collections::{BTreeMap, BTreeSet, HashMap}, fmt::{self, Debug}, net::{SocketAddr, SocketAddrV4, SocketAddrV6}, sync::Arc, @@ -38,6 +38,7 @@ mod ping; mod reportgen; pub use metrics::Metrics; +use reportgen::ProbeProto; pub use reportgen::QuicConfig; const FULL_REPORT_INTERVAL: Duration = Duration::from_secs(5 * 60); @@ -206,6 +207,140 @@ impl Default for Reports { } } +/// Options for running probes +/// +/// By default, will run icmp over IPv4, icmp over IPv6, and Https probes. +/// +/// Use [`Options::stun_v4`], [`Options::stun_v6`], and [`Options::quic_config`] +/// to enable STUN over IPv4, STUN over IPv6, and QUIC address discovery. +#[derive(Debug)] +pub struct Options { + /// Socket to send IPv4 STUN probes from. + /// + /// Responses are never read from this socket, they must be passed in via internal + /// messaging since, when used internally in iroh, the socket is also used to receive + /// other packets from in the magicsocket (`MagicSock`). + /// + /// If not provided, STUN probes will not be sent over IPv4. + stun_sock_v4: Option>, + /// Socket to send IPv6 STUN probes from. + /// + /// Responses are never read from this socket, they must be passed in via internal + /// messaging since, when used internally in iroh, the socket is also used to receive + /// other packets from in the magicsocket (`MagicSock`). + /// + /// If not provided, STUN probes will not be sent over IPv6. + stun_sock_v6: Option>, + /// The configuration needed to launch QUIC address discovery probes. + /// + /// If not provided, will not run QUIC address discovery. + quic_config: Option, + /// Enable icmp_v4 probes + /// + /// On by default + icmp_v4: bool, + /// Enable icmp_v6 probes + /// + /// On by default + icmp_v6: bool, + /// Enable https probes + /// + /// On by default + https: bool, +} + +impl Default for Options { + fn default() -> Self { + Self { + stun_sock_v4: None, + stun_sock_v6: None, + quic_config: None, + icmp_v4: true, + icmp_v6: true, + https: true, + } + } +} + +impl Options { + /// Create an empty Options that enables no probes + pub fn empty() -> Self { + Self { + stun_sock_v4: None, + stun_sock_v6: None, + quic_config: None, + icmp_v4: false, + icmp_v6: false, + https: false, + } + } + + /// Set the ipv4 stun socket and enable ipv4 stun probes + pub fn stun_v4(mut self, sock: Option>) -> Self { + self.stun_sock_v4 = sock; + self + } + + /// Set the ipv6 stun socket and enable ipv6 stun probes + pub fn stun_v6(mut self, sock: Option>) -> Self { + self.stun_sock_v6 = sock; + self + } + + /// Enable quic probes + pub fn quic_config(mut self, quic_config: Option) -> Self { + self.quic_config = quic_config; + self + } + + /// Enable icmp_v4 probe + pub fn enable_icmp_v4(mut self) -> Self { + self.icmp_v4 = true; + self + } + + /// Enable icmp_v6 probe + pub fn enable_icmp_v6(mut self) -> Self { + self.icmp_v6 = true; + self + } + + /// Enable https probe + pub fn enable_https(mut self) -> Self { + self.https = true; + self + } + + /// Turn the options into set of valid protocols + fn to_protocols(&self) -> BTreeSet { + let mut protocols = BTreeSet::new(); + if self.stun_sock_v4.is_some() { + protocols.insert(ProbeProto::StunIpv4); + } + if self.stun_sock_v6.is_some() { + protocols.insert(ProbeProto::StunIpv6); + } + if let Some(ref quic) = self.quic_config { + if quic.ipv4 { + protocols.insert(ProbeProto::QuicIpv4); + } + if quic.ipv6 { + protocols.insert(ProbeProto::QuicIpv6); + } + } + if self.icmp_v4 { + protocols.insert(ProbeProto::IcmpV4); + } + if self.icmp_v6 { + protocols.insert(ProbeProto::IcmpV6); + } + if self.https { + protocols.insert(ProbeProto::Https); + } + protocols + } +} + impl Client { /// Creates a new net_report client. /// @@ -252,16 +387,37 @@ impl Client { /// using QUIC address discovery. /// /// When `None`, it will disable the QUIC address discovery probes. + /// + /// This will attempt to use *all* probe protocols. pub async fn get_report( &mut self, - dm: RelayMap, - stun_conn4: Option>, - stun_conn6: Option>, + relay_map: RelayMap, + stun_sock_v4: Option>, + stun_sock_v6: Option>, quic_config: Option, ) -> Result> { - let rx = self - .get_report_channel(dm, stun_conn4, stun_conn6, quic_config) - .await?; + let opts = Options::default() + .stun_v4(stun_sock_v4) + .stun_v6(stun_sock_v6) + .quic_config(quic_config); + let rx = self.get_report_channel(relay_map.clone(), opts).await?; + match rx.await { + Ok(res) => res, + Err(_) => Err(anyhow!("channel closed, actor awol")), + } + } + + /// Runs a net_report, returning the report. + /// + /// It may not be called concurrently with itself, `&mut self` takes care of that. + /// + /// Look at [`Options`] for the different configuration options. + pub async fn get_report_with_opts( + &mut self, + relay_map: RelayMap, + opts: Options, + ) -> Result> { + let rx = self.get_report_channel(relay_map, opts).await?; match rx.await { Ok(res) => res, Err(_) => Err(anyhow!("channel closed, actor awol")), @@ -269,22 +425,18 @@ impl Client { } /// Get report with channel + /// + /// Look at [`Options`] for the different configuration options. pub async fn get_report_channel( &mut self, - dm: RelayMap, - stun_conn4: Option>, - stun_conn6: Option>, - quic_config: Option, + relay_map: RelayMap, + opts: Options, ) -> Result>>> { - // TODO: consider if RelayMap should be made to easily clone? It seems expensive - // right now. let (tx, rx) = oneshot::channel(); self.addr .send(Message::RunCheck { - relay_map: dm, - stun_sock_v4: stun_conn4, - stun_sock_v6: stun_conn6, - quic_config, + relay_map, + opts, response_tx: tx, }) .await?; @@ -310,25 +462,10 @@ pub(crate) enum Message { /// Only one net_report can be run at a time, trying to run multiple concurrently will /// fail. RunCheck { - /// The relay configuration. + /// The map of relays we want to probe relay_map: RelayMap, - /// Socket to send IPv4 STUN probes from. - /// - /// Responses are never read from this socket, they must be passed in via the - /// [`Message::StunPacket`] message since the socket is also used to receive - /// other packets from in the magicsocket (`MagicSock`). - /// - /// If not provided this will attempt to bind a suitable socket itself. - stun_sock_v4: Option>, - /// Socket to send IPv6 STUN probes from. - /// - /// Like `stun_sock_v4` but for IPv6. - stun_sock_v6: Option>, - /// Endpoint and client configuration to create a QUIC - /// connection to do QUIC address discovery. - /// - /// If not provided, will not do QUIC address discovery. - quic_config: Option, + /// Options for the report + opts: Options, /// Channel to receive the response. response_tx: oneshot::Sender>>, }, @@ -466,18 +603,10 @@ impl Actor { match msg { Message::RunCheck { relay_map, - stun_sock_v4, - stun_sock_v6, - quic_config, + opts, response_tx, } => { - self.handle_run_check( - relay_map, - stun_sock_v4, - stun_sock_v6, - quic_config, - response_tx, - ); + self.handle_run_check(relay_map, opts, response_tx); } Message::ReportReady { report } => { self.handle_report_ready(report); @@ -503,11 +632,16 @@ impl Actor { fn handle_run_check( &mut self, relay_map: RelayMap, - stun_sock_v4: Option>, - stun_sock_v6: Option>, - quic_config: Option, + opts: Options, response_tx: oneshot::Sender>>, ) { + let protocols = opts.to_protocols(); + let Options { + stun_sock_v4, + stun_sock_v6, + quic_config, + .. + } = opts; if self.current_report_run.is_some() { response_tx .send(Err(anyhow!( @@ -519,15 +653,6 @@ impl Actor { let now = Instant::now(); - let cancel_token = CancellationToken::new(); - let stun_sock_v4 = match stun_sock_v4 { - Some(sock) => Some(sock), - None => bind_local_stun_socket(IpFamily::V4, self.addr(), cancel_token.clone()), - }; - let stun_sock_v6 = match stun_sock_v6 { - Some(sock) => Some(sock), - None => bind_local_stun_socket(IpFamily::V6, self.addr(), cancel_token.clone()), - }; let mut do_full = self.reports.next_full || now.duration_since(self.reports.last_full) > FULL_REPORT_INTERVAL; @@ -558,11 +683,11 @@ impl Actor { stun_sock_v6, quic_config, self.dns_resolver.clone(), + protocols, ); self.current_report_run = Some(ReportRun { _reportgen: actor, - _drop_guard: cancel_token.drop_guard(), report_tx: response_tx, }); } @@ -729,8 +854,6 @@ impl Actor { struct ReportRun { /// The handle of the [`reportgen`] actor, cancels the actor on drop. _reportgen: reportgen::Client, - /// Drop guard to optionally kill workers started by net_report to support reportgen. - _drop_guard: tokio_util::sync::DropGuard, /// Where to send the completed report. report_tx: oneshot::Sender>>, } @@ -740,7 +863,7 @@ struct ReportRun { /// If successful this returns the bound socket and will forward STUN responses to the /// provided *actor_addr*. The *cancel_token* serves to stop the packet forwarding when the /// socket is no longer needed. -fn bind_local_stun_socket( +pub fn bind_local_stun_socket( network: IpFamily, actor_addr: Addr, cancel_token: CancellationToken, @@ -1003,8 +1126,10 @@ mod tests { // Note that the ProbePlan will change with each iteration. for i in 0..5 { + let cancel = CancellationToken::new(); + let sock = bind_local_stun_socket(IpFamily::V4, client.addr(), cancel.clone()); println!("--round {}", i); - let r = client.get_report(dm.clone(), None, None, None).await?; + let r = client.get_report(dm.clone(), sock, None, None).await?; assert!(r.udp, "want UDP"); assert_eq!( @@ -1020,6 +1145,7 @@ mod tests { ); assert!(r.global_v4.is_some(), "expected globalV4 set"); assert!(r.preferred_relay.is_some(),); + cancel.cancel(); } assert!( diff --git a/iroh-net-report/src/reportgen.rs b/iroh-net-report/src/reportgen.rs index 67491edcac..fb144cdc25 100644 --- a/iroh-net-report/src/reportgen.rs +++ b/iroh-net-report/src/reportgen.rs @@ -17,6 +17,7 @@ //! - Sends the completed report to the net_report actor. use std::{ + collections::BTreeSet, future::Future, net::{IpAddr, SocketAddr}, pin::Pin, @@ -59,7 +60,8 @@ use crate::{ mod hairpin; mod probes; -use probes::{Probe, ProbePlan, ProbeProto}; +pub use probes::ProbeProto; +use probes::{Probe, ProbePlan}; use crate::defaults::timeouts::{ CAPTIVE_PORTAL_DELAY, CAPTIVE_PORTAL_TIMEOUT, OVERALL_REPORT_TIMEOUT, PROBES_TIMEOUT, @@ -91,6 +93,7 @@ impl Client { stun_sock6: Option>, quic_config: Option, dns_resolver: DnsResolver, + protocols: BTreeSet, ) -> Self { let (msg_tx, msg_rx) = mpsc::channel(32); let addr = Addr { @@ -110,6 +113,7 @@ impl Client { hairpin_actor: hairpin::Client::new(net_report, addr), outstanding_tasks: OutstandingTasks::default(), dns_resolver, + protocols, }; let task = tokio::spawn( async move { actor.run().await }.instrument(info_span!("reportgen.actor")), @@ -193,6 +197,9 @@ struct Actor { outstanding_tasks: OutstandingTasks, /// The DNS resolver to use for probes that need to resolve DNS records. dns_resolver: DnsResolver, + /// Protocols we should attempt to create probes for, if we have the correct + /// configuration for that protocol. + protocols: BTreeSet, } impl Actor { @@ -536,8 +543,10 @@ impl Actor { let if_state = interfaces::State::new().await; debug!(%if_state, "Local interfaces"); let plan = match self.last_report { - Some(ref report) => ProbePlan::with_last_report(&self.relay_map, &if_state, report), - None => ProbePlan::initial(&self.relay_map, &if_state), + Some(ref report) => { + ProbePlan::with_last_report(&self.relay_map, &if_state, report, &self.protocols) + } + None => ProbePlan::initial(&self.relay_map, &if_state, &self.protocols), }; trace!(%plan, "probe plan"); @@ -687,6 +696,10 @@ pub struct QuicConfig { pub ep: quinn::Endpoint, /// A client config. pub client_config: rustls::ClientConfig, + /// Enable ipv4 QUIC address discovery probes + pub ipv4: bool, + /// Enable ipv6 QUIC address discovery probes + pub ipv6: bool, } /// Executes a particular [`Probe`], including using a delayed start if needed. @@ -790,7 +803,7 @@ async fn run_probe( let url = node.url.clone(); match quic_config { Some(quic_config) => { - run_quic_probe(quic_config, url, relay_addr, probe).await?; + result = run_quic_probe(quic_config, url, relay_addr, probe).await?; } None => { return Err(ProbeError::AbortSet( @@ -1577,6 +1590,8 @@ mod tests { let quic_addr_disc = QuicConfig { ep: ep.clone(), client_config, + ipv4: true, + ipv6: true, }; let url = relay.url.clone(); let port = server.quic_addr().unwrap().port(); diff --git a/iroh-net-report/src/reportgen/probes.rs b/iroh-net-report/src/reportgen/probes.rs index ac27bef23b..ba5f9ce22e 100644 --- a/iroh-net-report/src/reportgen/probes.rs +++ b/iroh-net-report/src/reportgen/probes.rs @@ -45,7 +45,7 @@ const NUM_INCREMENTAL_RELAYS: usize = 3; /// The protocol used to time a node's latency. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, derive_more::Display)] #[repr(u8)] -pub(super) enum ProbeProto { +pub enum ProbeProto { /// STUN IPv4 StunIpv4, /// STUN IPv6 @@ -214,16 +214,26 @@ impl fmt::Display for ProbeSet { /// /// [`reportgen`]: crate::reportgen #[derive(Debug, PartialEq, Eq)] -pub(super) struct ProbePlan(BTreeSet); +pub(super) struct ProbePlan { + set: BTreeSet, + protocols: BTreeSet, +} impl ProbePlan { /// Creates an initial probe plan. - pub(super) fn initial(relay_map: &RelayMap, if_state: &interfaces::State) -> Self { - let mut plan = Self(BTreeSet::new()); + pub(super) fn initial( + relay_map: &RelayMap, + if_state: &interfaces::State, + protocols: &BTreeSet, + ) -> Self { + let mut plan = Self { + set: BTreeSet::new(), + protocols: protocols.clone(), + }; // The first time we need add probes after the STUN we record this delay, so that // further relay server can reuse this delay. - let mut max_stun_delay: Option = None; + let mut max_high_prio_delay: Option = None; for relay_node in relay_map.nodes() { let mut stun_ipv4_probes = ProbeSet::new(ProbeProto::StunIpv4); @@ -263,20 +273,25 @@ impl ProbePlan { }) .expect("adding QuicIpv6 probe to a QuicAddrIpv6 probe set"); } - plan.add(stun_ipv4_probes); - plan.add(stun_ipv6_probes); - plan.add(quic_ipv4_probes); - plan.add(quic_ipv6_probes); + plan.add_if_enabled(stun_ipv4_probes); + plan.add_if_enabled(stun_ipv6_probes); + plan.add_if_enabled(quic_ipv4_probes); + plan.add_if_enabled(quic_ipv6_probes); // The HTTP and ICMP probes only start after the STUN probes have had a chance. let mut https_probes = ProbeSet::new(ProbeProto::Https); - let mut icmp_probes_ipv4 = ProbeSet::new(ProbeProto::IcmpV4); - let mut icmp_probes_ipv6 = ProbeSet::new(ProbeProto::IcmpV6); + let mut icmp_v4_probes = ProbeSet::new(ProbeProto::IcmpV4); + let mut icmp_v6_probes = ProbeSet::new(ProbeProto::IcmpV6); + for attempt in 0..3 { - let start = *max_stun_delay.get_or_insert_with(|| plan.max_delay()) - + DEFAULT_INITIAL_RETRANSMIT; + let mut start = *max_high_prio_delay.get_or_insert_with(|| plan.max_delay()); + // if there are high priority probes, ensure there is a buffer between + // the highest probe delay and the next probes we create + // if there are no high priority probes, we don't need a buffer + if plan.has_priority_probes() { + start += DEFAULT_INITIAL_RETRANSMIT; + } let delay = start + DEFAULT_INITIAL_RETRANSMIT * attempt as u32; - https_probes .push(Probe::Https { delay, @@ -284,7 +299,7 @@ impl ProbePlan { }) .expect("adding Https probe to a Https probe set"); if if_state.have_v4 { - icmp_probes_ipv4 + icmp_v4_probes .push(Probe::IcmpV4 { delay, node: relay_node.clone(), @@ -292,7 +307,7 @@ impl ProbePlan { .expect("adding Icmp probe to an Icmp probe set"); } if if_state.have_v6 { - icmp_probes_ipv6 + icmp_v6_probes .push(Probe::IcmpV6 { delay, node: relay_node.clone(), @@ -300,9 +315,10 @@ impl ProbePlan { .expect("adding IcmpIpv6 probe to and IcmpIpv6 probe set"); } } - plan.add(https_probes); - plan.add(icmp_probes_ipv4); - plan.add(icmp_probes_ipv6); + + plan.add_if_enabled(https_probes); + plan.add_if_enabled(icmp_v4_probes); + plan.add_if_enabled(icmp_v6_probes); } plan } @@ -312,11 +328,15 @@ impl ProbePlan { relay_map: &RelayMap, if_state: &interfaces::State, last_report: &Report, + protocols: &BTreeSet, ) -> Self { if last_report.relay_latency.is_empty() { - return Self::initial(relay_map, if_state); + return Self::initial(relay_map, if_state, protocols); } - let mut plan = Self(Default::default()); + let mut plan = Self { + set: Default::default(), + protocols: protocols.clone(), + }; // The first time we need add probes after the STUN we record this delay, so that // further relay servers can reuse this delay. @@ -400,10 +420,10 @@ impl ProbePlan { .expect("adding QuicIpv6 probe to a QuicAddrIpv6 probe set"); } } - plan.add(stun_ipv4_probes); - plan.add(stun_ipv6_probes); - plan.add(quic_ipv4_probes); - plan.add(quic_ipv6_probes); + plan.add_if_enabled(stun_ipv4_probes); + plan.add_if_enabled(stun_ipv6_probes); + plan.add_if_enabled(quic_ipv4_probes); + plan.add_if_enabled(quic_ipv6_probes); // The HTTP and ICMP probes only start after the STUN probes have had a chance. let mut https_probes = ProbeSet::new(ProbeProto::Https); @@ -437,40 +457,58 @@ impl ProbePlan { .expect("Pusying IcmpV6 Probe to an IcmpV6 ProbeSet"); } } - plan.add(https_probes); - plan.add(icmp_v4_probes); - plan.add(icmp_v6_probes); + + plan.add_if_enabled(https_probes); + plan.add_if_enabled(icmp_v4_probes); + plan.add_if_enabled(icmp_v6_probes); } plan } /// Returns an iterator over the [`ProbeSet`]s in this plan. pub(super) fn iter(&self) -> impl Iterator { - self.0.iter() + self.set.iter() } - /// Adds a [`ProbeSet`] if it contains probes. - fn add(&mut self, set: ProbeSet) { - if !set.is_empty() { - self.0.insert(set); + /// Adds a [`ProbeSet`] if it contains probes and the protocol indicated in + /// the [`ProbeSet] matches a protocol in our set of [`ProbeProto`]s. + fn add_if_enabled(&mut self, set: ProbeSet) { + if !set.is_empty() && self.protocols.contains(&set.proto) { + self.set.insert(set); } } /// Returns the delay of the last probe in the probe plan. fn max_delay(&self) -> Duration { - self.0 + self.set .iter() .flatten() .map(|probe| probe.delay()) .max() .unwrap_or_default() } + + /// Stun & Quic probes are "priority" probes + fn has_priority_probes(&self) -> bool { + for probe in &self.set { + if matches!( + probe.proto, + ProbeProto::StunIpv4 + | ProbeProto::StunIpv6 + | ProbeProto::QuicIpv4 + | ProbeProto::QuicIpv6 + ) { + return true; + } + } + false + } } impl fmt::Display for ProbePlan { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "ProbePlan {{")?; - for probe_set in self.0.iter() { + for probe_set in self.set.iter() { writeln!(f, r#" ProbeSet("{}") {{"#, probe_set.proto)?; for probe in probe_set.probes.iter() { writeln!(f, " {probe},")?; @@ -483,7 +521,10 @@ impl fmt::Display for ProbePlan { impl FromIterator for ProbePlan { fn from_iter>(iter: T) -> Self { - Self(iter.into_iter().collect()) + Self { + set: iter.into_iter().collect(), + protocols: BTreeSet::new(), + } } } @@ -549,15 +590,27 @@ mod tests { }; } + fn default_protocols() -> BTreeSet { + BTreeSet::from([ + ProbeProto::StunIpv4, + ProbeProto::StunIpv6, + ProbeProto::QuicIpv4, + ProbeProto::QuicIpv6, + ProbeProto::IcmpV4, + ProbeProto::IcmpV6, + ProbeProto::Https, + ]) + } + #[tokio::test] async fn test_initial_probeplan() { let (_servers, relay_map) = test_utils::relay_map(2).await; let relay_node_1 = relay_map.nodes().next().unwrap(); let relay_node_2 = relay_map.nodes().nth(1).unwrap(); let if_state = interfaces::State::fake(); - let plan = ProbePlan::initial(&relay_map, &if_state); + let plan = ProbePlan::initial(&relay_map, &if_state, &default_protocols()); - let expected_plan: ProbePlan = [ + let mut expected_plan: ProbePlan = [ probeset! { proto: ProbeProto::StunIpv4, relay: relay_node_1.clone(), @@ -659,6 +712,78 @@ mod tests { ] .into_iter() .collect(); + expected_plan.protocols = default_protocols(); + + println!("expected:"); + println!("{expected_plan}"); + println!("actual:"); + println!("{plan}"); + // The readable error: + assert_eq!(plan.to_string(), expected_plan.to_string()); + // Just in case there's a bug in the Display impl: + assert_eq!(plan, expected_plan); + } + + #[tokio::test] + async fn test_initial_probeplan_some_protocols() { + let (_servers, relay_map) = test_utils::relay_map(2).await; + let relay_node_1 = relay_map.nodes().next().unwrap(); + let relay_node_2 = relay_map.nodes().nth(1).unwrap(); + let if_state = interfaces::State::fake(); + let plan = ProbePlan::initial( + &relay_map, + &if_state, + &BTreeSet::from([ProbeProto::Https, ProbeProto::IcmpV4, ProbeProto::IcmpV6]), + ); + + let mut expected_plan: ProbePlan = [ + probeset! { + proto: ProbeProto::Https, + relay: relay_node_1.clone(), + delays: [Duration::ZERO, + Duration::from_millis(100), + Duration::from_millis(200)], + }, + probeset! { + proto: ProbeProto::IcmpV4, + relay: relay_node_1.clone(), + delays: [Duration::ZERO, + Duration::from_millis(100), + Duration::from_millis(200)], + }, + probeset! { + proto: ProbeProto::IcmpV6, + relay: relay_node_1.clone(), + delays: [Duration::ZERO, + Duration::from_millis(100), + Duration::from_millis(200)], + }, + probeset! { + proto: ProbeProto::Https, + relay: relay_node_2.clone(), + delays: [Duration::ZERO, + Duration::from_millis(100), + Duration::from_millis(200)], + }, + probeset! { + proto: ProbeProto::IcmpV4, + relay: relay_node_2.clone(), + delays: [Duration::ZERO, + Duration::from_millis(100), + Duration::from_millis(200)], + }, + probeset! { + proto: ProbeProto::IcmpV6, + relay: relay_node_2.clone(), + delays: [Duration::ZERO, + Duration::from_millis(100), + Duration::from_millis(200)], + }, + ] + .into_iter() + .collect(); + expected_plan.protocols = + BTreeSet::from([ProbeProto::Https, ProbeProto::IcmpV4, ProbeProto::IcmpV6]); println!("expected:"); println!("{expected_plan}"); @@ -704,8 +829,13 @@ mod tests { global_v6: None, captive_portal: None, }; - let plan = ProbePlan::with_last_report(&relay_map, &if_state, &last_report); - let expected_plan: ProbePlan = [ + let plan = ProbePlan::with_last_report( + &relay_map, + &if_state, + &last_report, + &default_protocols(), + ); + let mut expected_plan: ProbePlan = [ probeset! { proto: ProbeProto::StunIpv4, relay: relay_node_1.clone(), @@ -807,6 +937,7 @@ mod tests { ] .into_iter() .collect(); + expected_plan.protocols = default_protocols(); println!("{} round", i); println!("expected:"); diff --git a/iroh-relay/src/quic.rs b/iroh-relay/src/quic.rs index 760bbda5ca..81c22a1572 100644 --- a/iroh-relay/src/quic.rs +++ b/iroh-relay/src/quic.rs @@ -260,10 +260,7 @@ impl QuicClient { let mut observed_addr = res.expect("checked"); // if we've sent to an ipv4 address, but received an observed address // that is ivp6 then the address is an [IPv4-Mapped IPv6 Addresses](https://doc.rust-lang.org/beta/std/net/struct.Ipv6Addr.html#ipv4-mapped-ipv6-addresses) - if server_addr.is_ipv4() && observed_addr.is_ipv6() { - observed_addr = - SocketAddr::new(observed_addr.ip().to_canonical(), observed_addr.port()); - } + observed_addr = SocketAddr::new(observed_addr.ip().to_canonical(), observed_addr.port()); let latency = conn.rtt() / 2; // gracefully close the connections conn.close(QUIC_ADDR_DISC_CLOSE_CODE, QUIC_ADDR_DISC_CLOSE_REASON); diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index 23d24461d0..31ac05c8b6 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -2339,17 +2339,12 @@ impl Actor { } let relay_map = self.msock.relay_map.clone(); - let pconn4 = Some(self.pconn4.clone()); - let pconn6 = self.pconn6.clone(); - - let quic_config = None; + let opts = net_report::Options::default() + .stun_v4(Some(self.pconn4.clone())) + .stun_v6(self.pconn6.clone()); debug!("requesting net_report report"); - match self - .net_reporter - .get_report_channel(relay_map, pconn4, pconn6, quic_config) - .await - { + match self.net_reporter.get_report_channel(relay_map, opts).await { Ok(rx) => { let msg_sender = self.msg_sender.clone(); tokio::task::spawn(async move {