Skip to main content

nd_300/diagnostics/
mtu.rs

1use serde::Serialize;
2
3#[derive(Debug, Clone, Serialize)]
4pub struct MtuInfo {
5    pub interface: String,
6    pub mtu: u32,
7}
8
9pub async fn collect() -> Option<Vec<MtuInfo>> {
10    let mut results = Vec::new();
11
12    #[cfg(windows)]
13    {
14        let mut cmd = tokio::process::Command::new("netsh");
15        cmd.args(["interface", "ipv4", "show", "subinterfaces"]);
16        if let Some(output) = super::util::run_with_timeout(cmd, super::util::QUICK).await {
17            let text = String::from_utf8_lossy(&output.stdout);
18            for line in text.lines() {
19                let parts: Vec<&str> = line.split_whitespace().collect();
20                // Format: MTU  MediaSenseState   Bytes In  Bytes Out  Interface
21                if parts.len() >= 5 {
22                    if let Ok(mtu) = parts[0].parse::<u32>() {
23                        let iface = parts[4..].join(" ");
24                        results.push(MtuInfo {
25                            interface: iface,
26                            mtu,
27                        });
28                    }
29                }
30            }
31        }
32    }
33
34    #[cfg(target_os = "linux")]
35    {
36        let mut cmd = tokio::process::Command::new("ip");
37        cmd.args(["link", "show"]);
38        if let Some(output) = super::util::run_with_timeout(cmd, super::util::QUICK).await {
39            let text = String::from_utf8_lossy(&output.stdout);
40            for line in text.lines() {
41                if line.contains("mtu") {
42                    let parts: Vec<&str> = line.split_whitespace().collect();
43                    if let Some(name_idx) = parts
44                        .iter()
45                        .position(|p| p.ends_with(':') && !p.starts_with(' '))
46                    {
47                        let name = parts[name_idx].trim_end_matches(':');
48                        if let Some(mtu_idx) = parts.iter().position(|p| *p == "mtu") {
49                            if let Some(mtu_val) = parts.get(mtu_idx + 1) {
50                                if let Ok(mtu) = mtu_val.parse::<u32>() {
51                                    results.push(MtuInfo {
52                                        interface: name.to_string(),
53                                        mtu,
54                                    });
55                                }
56                            }
57                        }
58                    }
59                }
60            }
61        }
62    }
63
64    #[cfg(target_os = "macos")]
65    {
66        let cmd = tokio::process::Command::new("ifconfig");
67        if let Some(output) = super::util::run_with_timeout(cmd, super::util::QUICK).await {
68            let text = String::from_utf8_lossy(&output.stdout);
69            let mut current_iface = String::new();
70
71            for line in text.lines() {
72                if !line.starts_with('\t') && !line.starts_with(' ') {
73                    current_iface = line.split(':').next().unwrap_or("").to_string();
74                }
75                if line.contains("mtu") {
76                    let parts: Vec<&str> = line.split_whitespace().collect();
77                    if let Some(mtu_idx) = parts.iter().position(|p| *p == "mtu") {
78                        if let Some(mtu_val) = parts.get(mtu_idx + 1) {
79                            if let Ok(mtu) = mtu_val.parse::<u32>() {
80                                results.push(MtuInfo {
81                                    interface: current_iface.clone(),
82                                    mtu,
83                                });
84                            }
85                        }
86                    }
87                }
88            }
89        }
90    }
91
92    if results.is_empty() {
93        None
94    } else {
95        Some(results)
96    }
97}