From cb0df51319f474712f8b1108a9e7240d69e738cc Mon Sep 17 00:00:00 2001 From: "Sijie.Sun" Date: Sat, 9 Mar 2024 00:24:16 +0800 Subject: [PATCH] fix ip & route cfg on windows (#28) --- easytier-core/Cargo.toml | 2 + easytier-core/src/arch/windows.rs | 26 ++++---- easytier-core/src/common/ifcfg.rs | 73 ++++++++++++++++------- easytier-core/src/instance/virtual_nic.rs | 1 + easytier-core/src/peer_center/instance.rs | 2 +- easytier-core/src/peers/peer_manager.rs | 4 +- easytier-core/src/tunnels/common.rs | 12 +++- 7 files changed, 82 insertions(+), 38 deletions(-) diff --git a/easytier-core/Cargo.toml b/easytier-core/Cargo.toml index 53605d2..696d464 100644 --- a/easytier-core/Cargo.toml +++ b/easytier-core/Cargo.toml @@ -90,6 +90,8 @@ clap = { version = "4.4", features = ["derive"] } async-recursion = "1.0.5" +network-interface = "1.1.1" + [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.52", features = [ "Win32_Networking_WinSock", diff --git a/easytier-core/src/arch/windows.rs b/easytier-core/src/arch/windows.rs index d3040e6..46b4f3e 100644 --- a/easytier-core/src/arch/windows.rs +++ b/easytier-core/src/arch/windows.rs @@ -7,6 +7,7 @@ use std::{ ptr, }; +use network_interface::NetworkInterfaceConfig; use windows_sys::{ core::PCSTR, Win32::{ @@ -61,18 +62,21 @@ pub fn disable_connection_reset(socket: &S) -> io::Result<()> { Ok(()) } -pub fn find_interface_index_cached(iface_name: &str) -> io::Result { - let ifaces = pnet::datalink::interfaces(); - for iface in ifaces { - if iface.name == iface_name { - return Ok(iface.index); - } +pub fn find_interface_index(iface_name: &str) -> io::Result { + let ifaces = network_interface::NetworkInterface::show().map_err(|e| { + io::Error::new( + ErrorKind::NotFound, + format!("Failed to get interfaces. {}, error: {}", iface_name, e), + ) + })?; + if let Some(iface) = ifaces.iter().find(|iface| iface.name == iface_name) { + return Ok(iface.index); } - let err = io::Error::new( + tracing::error!("Failed to find interface index for {}", iface_name); + Err(io::Error::new( ErrorKind::NotFound, - format!("Failed to find interface index for {}", iface_name), - ); - Err(err) + format!("{}", iface_name), + )) } pub fn set_ip_unicast_if( @@ -82,7 +86,7 @@ pub fn set_ip_unicast_if( ) -> io::Result<()> { let handle = socket.as_raw_socket() as SOCKET; - let if_index = find_interface_index_cached(iface)?; + let if_index = find_interface_index(iface)?; unsafe { // https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options diff --git a/easytier-core/src/common/ifcfg.rs b/easytier-core/src/common/ifcfg.rs index 38e5f57..0bf3fd5 100644 --- a/easytier-core/src/common/ifcfg.rs +++ b/easytier-core/src/common/ifcfg.rs @@ -202,7 +202,46 @@ pub struct WindowsIfConfiger {} #[cfg(target_os = "windows")] impl WindowsIfConfiger { pub fn get_interface_index(name: &str) -> Option { - crate::arch::windows::find_interface_index_cached(name).ok() + crate::arch::windows::find_interface_index(name).ok() + } + + async fn list_ipv4(name: &str) -> Result, Error> { + use anyhow::Context; + use network_interface::NetworkInterfaceConfig; + use std::net::IpAddr; + let ret = network_interface::NetworkInterface::show().with_context(|| "show interface")?; + let addrs = ret + .iter() + .filter_map(|x| { + if x.name != name { + return None; + } + Some(x.addr.clone()) + }) + .flat_map(|x| x) + .map(|x| x.ip()) + .filter_map(|x| { + if let IpAddr::V4(ipv4) = x { + Some(ipv4) + } else { + None + } + }) + .collect::>(); + + Ok(addrs) + } + + async fn remove_one_ipv4(name: &str, ip: Ipv4Addr) -> Result<(), Error> { + run_shell_cmd( + format!( + "netsh interface ipv4 delete address {} address={}", + name, + ip.to_string() + ) + .as_str(), + ) + .await } } @@ -283,17 +322,12 @@ impl IfConfiguerTrait for WindowsIfConfiger { async fn remove_ip(&self, name: &str, ip: Option) -> Result<(), Error> { if ip.is_none() { - run_shell_cmd(format!("netsh interface ipv4 delete address {}", name).as_str()).await + for ip in Self::list_ipv4(name).await?.iter() { + Self::remove_one_ipv4(name, *ip).await?; + } + Ok(()) } else { - run_shell_cmd( - format!( - "netsh interface ipv4 delete address {} address={}", - name, - ip.unwrap().to_string() - ) - .as_str(), - ) - .await + Self::remove_one_ipv4(name, ip.unwrap()).await } } @@ -301,18 +335,15 @@ impl IfConfiguerTrait for WindowsIfConfiger { Ok( tokio::time::timeout(std::time::Duration::from_secs(10), async move { loop { - let Ok(_) = run_shell_cmd( - format!("netsh interface ipv4 show interfaces {}", name).as_str(), - ) - .await - else { - tokio::time::sleep(std::time::Duration::from_millis(100)).await; - continue; - }; - break; + if let Some(idx) = Self::get_interface_index(name) { + tracing::info!(?name, ?idx, "Interface found"); + break; + } + tokio::time::sleep(std::time::Duration::from_millis(100)).await; } + Ok::<(), Error>(()) }) - .await?, + .await??, ) } } diff --git a/easytier-core/src/instance/virtual_nic.rs b/easytier-core/src/instance/virtual_nic.rs index 870f2b5..586cf82 100644 --- a/easytier-core/src/instance/virtual_nic.rs +++ b/easytier-core/src/instance/virtual_nic.rs @@ -54,6 +54,7 @@ impl VirtualNic { let mut config = tun::Configuration::default(); let has_packet_info = cfg!(target_os = "macos"); config.layer(tun::Layer::L3); + config.name(format!("et_{}", self.global_ctx.inst_name)); #[cfg(target_os = "linux")] { diff --git a/easytier-core/src/peer_center/instance.rs b/easytier-core/src/peer_center/instance.rs index 064234c..4096e55 100644 --- a/easytier-core/src/peer_center/instance.rs +++ b/easytier-core/src/peer_center/instance.rs @@ -83,7 +83,7 @@ impl PeerCenterBase { }); loop { let Some(center_peer) = Self::select_center_peer(&peer_mgr).await else { - tracing::warn!("no center peer found, sleep 1 second"); + tracing::trace!("no center peer found, sleep 1 second"); tokio::time::sleep(Duration::from_secs(1)).await; continue; }; diff --git a/easytier-core/src/peers/peer_manager.rs b/easytier-core/src/peers/peer_manager.rs index 32539f4..e5cb982 100644 --- a/easytier-core/src/peers/peer_manager.rs +++ b/easytier-core/src/peers/peer_manager.rs @@ -416,7 +416,7 @@ impl PeerManager { let mut dst_peers = vec![]; // NOTE: currently we only support ipv4 and cidr is 24 - if ipv4_addr.octets()[3] == 255 { + if ipv4_addr.is_broadcast() || ipv4_addr.is_multicast() || ipv4_addr.octets()[3] == 255 { dst_peers.extend( self.peers .list_routes() @@ -429,7 +429,7 @@ impl PeerManager { } if dst_peers.is_empty() { - log::error!("no peer id for ipv4: {}", ipv4_addr); + tracing::info!("no peer id for ipv4: {}", ipv4_addr); return Ok(()); } diff --git a/easytier-core/src/tunnels/common.rs b/easytier-core/src/tunnels/common.rs index e8ef12e..682da1f 100644 --- a/easytier-core/src/tunnels/common.rs +++ b/easytier-core/src/tunnels/common.rs @@ -7,6 +7,7 @@ use std::{ use async_stream::stream; use futures::{Future, FutureExt, Sink, SinkExt, Stream, StreamExt}; +use network_interface::NetworkInterfaceConfig; use tokio::{sync::Mutex, time::error::Elapsed}; use std::pin::Pin; @@ -258,14 +259,19 @@ impl Tunnel for TunnelWithCustomInfo { } pub(crate) fn get_interface_name_by_ip(local_ip: &IpAddr) -> Option { - let ifaces = pnet::datalink::interfaces(); + if local_ip.is_unspecified() || local_ip.is_multicast() { + return None; + } + let ifaces = network_interface::NetworkInterface::show().ok()?; for iface in ifaces { - for ip in iface.ips { - if ip.ip() == *local_ip { + for addr in iface.addr { + if addr.ip() == *local_ip { return Some(iface.name); } } } + + tracing::error!(?local_ip, "can not find interface name by ip"); None }