iroh_services/
net_diagnostics.rs1use std::net::SocketAddr;
11
12use iroh::NetReport;
13use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct DiagnosticsReport {
17 pub endpoint_id: iroh::EndpointId,
18 pub net_report: Option<NetReport>,
19 pub direct_addrs: Vec<SocketAddr>,
20 pub portmap_probe: Option<PortMapProbe>,
21 #[serde(default)]
22 pub iroh_version: String,
23 #[serde(default)]
24 pub iroh_services_version: String,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29pub struct PortMapProbe {
30 pub upnp: bool,
31 pub pcp: bool,
32 pub nat_pmp: bool,
33}
34
35#[cfg(feature = "net_diagnostics")]
36pub mod checks {
37 use std::{net::SocketAddr, time::Duration};
38
39 use anyhow::Result;
40 use iroh::{Endpoint, Watcher};
41
42 use super::*;
43
44 pub async fn run_diagnostics(endpoint: &Endpoint) -> Result<DiagnosticsReport> {
46 run_diagnostics_with_timeout(endpoint, Duration::from_secs(10)).await
47 }
48
49 async fn run_diagnostics_with_timeout(
51 endpoint: &Endpoint,
52 timeout: Duration,
53 ) -> Result<DiagnosticsReport> {
54 let endpoint_id = endpoint.id();
55
56 if tokio::time::timeout(timeout, endpoint.online())
58 .await
59 .is_err()
60 {
61 tracing::warn!("waiting for relay connection timed out after {timeout:?}");
62 }
63
64 let mut watcher = endpoint.net_report();
66 let net_report = match tokio::time::timeout(timeout, watcher.initialized()).await {
67 Ok(report) => Some(report),
68 Err(_) => {
69 tracing::warn!("net report timed out after {timeout:?}, using partial data");
70 watcher.get()
71 }
72 };
73
74 let addr = endpoint.addr();
76 let direct_addrs: Vec<SocketAddr> = addr.ip_addrs().copied().collect();
77
78 let portmap_probe =
80 match tokio::time::timeout(Duration::from_secs(5), probe_port_mapping()).await {
81 Ok(Ok(p)) => Some(p),
82 Ok(Err(e)) => {
83 tracing::warn!("portmap probe failed: {e}");
84 None
85 }
86 Err(_) => {
87 tracing::warn!("portmap probe timed out");
88 None
89 }
90 };
91
92 Ok(DiagnosticsReport {
93 endpoint_id,
94 net_report,
95 direct_addrs,
96 portmap_probe,
97 iroh_version: crate::IROH_VERSION.to_string(),
98 iroh_services_version: crate::IROH_SERVICES_VERSION.to_string(),
99 })
100 }
101
102 async fn probe_port_mapping() -> Result<PortMapProbe> {
103 let config = portmapper::Config {
104 enable_upnp: true,
105 enable_pcp: true,
106 enable_nat_pmp: true,
107 protocol: portmapper::Protocol::Udp,
108 };
109 let client = portmapper::Client::new(config);
110 let probe_rx = client.probe();
111 let probe = probe_rx.await?.map_err(|e| anyhow::anyhow!(e))?;
112 Ok(PortMapProbe {
113 upnp: probe.upnp,
114 pcp: probe.pcp,
115 nat_pmp: probe.nat_pmp,
116 })
117 }
118}
119
120#[cfg(test)]
121#[cfg(feature = "net_diagnostics")]
122mod tests {
123 use crate::run_diagnostics;
124
125 #[tokio::test]
126 async fn test_run_diagnostics() {
127 let endpoint = iroh::Endpoint::empty_builder(iroh::RelayMode::Disabled)
128 .bind()
129 .await
130 .unwrap();
131 run_diagnostics(&endpoint).await.unwrap();
132 endpoint.close().await;
133 }
134}