diff --git a/agent/Cargo.toml b/agent/Cargo.toml index f87de66..f4c3b0c 100644 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nanometrics-agent" -version = "0.1.8" +version = "0.1.9" edition = "2021" [lib] diff --git a/agent/src/main.rs b/agent/src/main.rs index e2a9790..36f5b52 100644 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -10,22 +10,66 @@ extern "C" fn handle_signal(_: libc::c_int) { RUNNING.store(false, Ordering::Relaxed); } +fn physical_ipv4_addrs() -> Vec { + let mut result = Vec::new(); + unsafe { + let mut ifap = std::ptr::null_mut::(); + if libc::getifaddrs(&mut ifap) != 0 { return result; } + let mut ifa = ifap; + while !ifa.is_null() { + let flags = (*ifa).ifa_flags as i32; + let up = flags & libc::IFF_UP as i32 != 0; + let loopback = flags & libc::IFF_LOOPBACK as i32 != 0; + let pointop = flags & libc::IFF_POINTOPOINT as i32 != 0; + if !up || loopback || pointop { ifa = (*ifa).ifa_next; continue; } + + let name = std::ffi::CStr::from_ptr((*ifa).ifa_name) + .to_string_lossy().into_owned(); + + // Type 1 = ARPHRD_ETHER (Ethernet + WiFi), exclut WireGuard (65534), tunnels, etc. + let itype: u32 = std::fs::read_to_string( + format!("/sys/class/net/{}/type", name)) + .ok().and_then(|s| s.trim().parse().ok()).unwrap_or(0); + if itype != 1 { ifa = (*ifa).ifa_next; continue; } + + // Exclut bridges et interfaces Docker par nom + let is_virtual = name.starts_with("br-") || name.starts_with("docker") + || name.starts_with("virbr") || name.starts_with("veth"); + if is_virtual { ifa = (*ifa).ifa_next; continue; } + + if let Some(addr) = (*ifa).ifa_addr.as_ref() { + if addr.sa_family as i32 == libc::AF_INET { + let sin = addr as *const _ as *const libc::sockaddr_in; + let b = (*sin).sin_addr.s_addr.to_ne_bytes(); + result.push(format!("{}.{}.{}.{}", b[0], b[1], b[2], b[3])); + } + } + ifa = (*ifa).ifa_next; + } + libc::freeifaddrs(ifap); + } + result +} + fn get_local_ip(server_ip: &str) -> String { + let physical = physical_ipv4_addrs(); + use std::net::UdpSocket; - // Try server IP first (always reachable), then internet fallback for target in &[format!("{}:80", server_ip), "8.8.8.8:80".to_string()] { if let Ok(s) = UdpSocket::bind("0.0.0.0:0") { if s.connect(target.as_str()).is_ok() { if let Ok(addr) = s.local_addr() { let ip = addr.ip().to_string(); - if ip != "0.0.0.0" { + // N'accepte que si c'est une vraie interface physique + if ip != "0.0.0.0" && physical.contains(&ip) { return ip; } } } } } - "0.0.0.0".to_string() + // Fallback : première IP physique disponible + physical.into_iter().next().unwrap_or_else(|| "0.0.0.0".to_string()) } fn apply_config_update(cfg: &mut config::Config, data: &[u8]) { diff --git a/agent/src/metrics/network_info.rs b/agent/src/metrics/network_info.rs index 757119f..f410e09 100644 --- a/agent/src/metrics/network_info.rs +++ b/agent/src/metrics/network_info.rs @@ -26,11 +26,13 @@ pub fn current_yday() -> u32 { } fn is_physical(name: &str) -> bool { - if name == "lo" { return false; } - for prefix in &["veth", "docker", "br-", "virbr", "vir", "tun", "tap", "dummy", "bond"] { - if name.starts_with(prefix) { return false; } - } - true + // Type 1 = ARPHRD_ETHER (Ethernet + WiFi). WireGuard = 65534, tunnels = autres. + let itype: u32 = std::fs::read_to_string(format!("/sys/class/net/{}/type", name)) + .ok().and_then(|s| s.trim().parse().ok()).unwrap_or(0); + if itype != 1 { return false; } + // Exclut bridges et interfaces Docker par nom (type 1 aussi) + !name.starts_with("br-") && !name.starts_with("docker") + && !name.starts_with("virbr") && !name.starts_with("veth") } fn read_sysfs(iface: &str, file: &str) -> Option { diff --git a/dist/nanometrics-agent-linux-amd64 b/dist/nanometrics-agent-linux-amd64 index 23c5d20..d5ce209 100755 Binary files a/dist/nanometrics-agent-linux-amd64 and b/dist/nanometrics-agent-linux-amd64 differ diff --git a/dist/nanometrics-agent-linux-arm64 b/dist/nanometrics-agent-linux-arm64 index d8c2d39..b4ca9ae 100755 Binary files a/dist/nanometrics-agent-linux-arm64 and b/dist/nanometrics-agent-linux-arm64 differ