diff --git a/easytier/Cargo.toml b/easytier/Cargo.toml index 45d779a..b24bc7d 100644 --- a/easytier/Cargo.toml +++ b/easytier/Cargo.toml @@ -89,7 +89,7 @@ tun = { package = "tun-easytier", git="https://github.com/EasyTier/rust-tun", fe "async", ], optional = true } # for net ns -nix = { version = "0.29.0", features = ["sched", "socket", "ioctl", "net"] } +nix = { version = "0.29.0", features = ["sched", "socket", "ioctl", "net", "fs"] } uuid = { version = "1.5.0", features = [ "v4", diff --git a/easytier/src/instance/virtual_nic.rs b/easytier/src/instance/virtual_nic.rs index efb0574..cb5722c 100644 --- a/easytier/src/instance/virtual_nic.rs +++ b/easytier/src/instance/virtual_nic.rs @@ -255,7 +255,10 @@ impl Drop for VirtualNic { if let Some(ref ifname) = self.ifname { // Try to clean up firewall rules, but don't panic in destructor if let Err(e) = crate::arch::windows::remove_interface_firewall_rules(ifname) { - eprintln!("Warning: Failed to remove firewall rules for interface {}: {}", ifname, e); + eprintln!( + "Warning: Failed to remove firewall rules for interface {}: {}", + ifname, e + ); } } } @@ -271,12 +274,113 @@ impl VirtualNic { } } + /// Check and create TUN device node if necessary on Linux systems + #[cfg(target_os = "linux")] + async fn ensure_tun_device_node() { + const TUN_DEV_PATH: &str = "/dev/net/tun"; + const TUN_DIR_PATH: &str = "/dev/net"; + + // Check if /dev/net/tun already exists + if tokio::fs::metadata(TUN_DEV_PATH).await.is_ok() { + tracing::debug!("TUN device node {} already exists", TUN_DEV_PATH); + return; + } + + tracing::info!( + "TUN device node {} not found, attempting to create", + TUN_DEV_PATH + ); + + // Check if TUN kernel module is available + let tun_module_available = tokio::fs::metadata("/proc/net/dev").await.is_ok() + && (tokio::fs::read_to_string("/proc/modules").await) + .map(|content| content.contains("tun")) + .unwrap_or(false); + + if !tun_module_available { + tracing::warn!("TUN kernel module may not be loaded"); + println!("⚠ Warning: TUN kernel module may not be available."); + println!(" You may need to load it with: sudo modprobe tun"); + } + + // Try to create /dev/net directory if it doesn't exist + if tokio::fs::metadata(TUN_DIR_PATH).await.is_err() { + if let Err(e) = tokio::fs::create_dir_all(TUN_DIR_PATH).await { + tracing::warn!( + "Failed to create directory {}: {}. Continuing anyway.", + TUN_DIR_PATH, + e + ); + println!( + "⚠ Warning: Failed to create directory {}. TUN device creation may fail.", + TUN_DIR_PATH + ); + println!( + " You may need to run with root privileges or manually create the TUN device." + ); + Self::print_troubleshooting_info(); + return; + } + tracing::info!("Created directory {}", TUN_DIR_PATH); + } + + // Try to create the TUN device node + // Major number 10, minor number 200 for /dev/net/tun + let dev_node = nix::sys::stat::makedev(10, 200); + + match nix::sys::stat::mknod( + TUN_DEV_PATH, + nix::sys::stat::SFlag::S_IFCHR, + nix::sys::stat::Mode::from_bits(0o600).unwrap(), + dev_node, + ) { + Ok(_) => { + tracing::info!("Successfully created TUN device node {}", TUN_DEV_PATH); + println!("✓ Created TUN device node {}", TUN_DEV_PATH); + } + Err(e) => { + tracing::warn!( + "Failed to create TUN device node {}: {}. Continuing anyway.", + TUN_DEV_PATH, + e + ); + println!( + "⚠ Warning: Failed to create TUN device node {}.", + TUN_DEV_PATH + ); + println!(" Error: {}", e); + Self::print_troubleshooting_info(); + } + } + } + + /// Print troubleshooting information for TUN device issues + #[cfg(target_os = "linux")] + fn print_troubleshooting_info() { + println!(" Possible solutions:"); + println!(" 1. Run with root privileges: sudo ./easytier-core [options]"); + println!(" 2. Manually create TUN device: sudo mkdir -p /dev/net && sudo mknod /dev/net/tun c 10 200"); + println!(" 3. Load TUN kernel module: sudo modprobe tun"); + println!(" 4. Use --no-tun flag if TUN functionality is not needed"); + println!(" 5. Check if your system/container supports TUN devices"); + println!(" Note: TUN functionality may still work if the kernel supports dynamic device creation."); + } + + /// For non-Linux systems, this is a no-op + #[cfg(not(target_os = "linux"))] + async fn ensure_tun_device_node() -> Result<(), Error> { + Ok(()) + } + async fn create_tun(&mut self) -> Result { let mut config = Configuration::default(); config.layer(Layer::L3); #[cfg(target_os = "linux")] { + // Check and create TUN device node if necessary (Linux only) + Self::ensure_tun_device_node().await; + let dev_name = self.global_ctx.get_flags().dev_name; if !dev_name.is_empty() { config.tun_name(format!("{}", dev_name)); @@ -434,15 +538,28 @@ impl VirtualNic { // Add firewall rules for virtual NIC interface to allow all traffic match crate::arch::windows::add_interface_to_firewall_allowlist(&ifname) { Ok(_) => { - tracing::info!("Successfully configured Windows Firewall for interface: {}", ifname); - tracing::info!("All protocols (TCP/UDP/ICMP) are now allowed on interface: {}", ifname); - }, + tracing::info!( + "Successfully configured Windows Firewall for interface: {}", + ifname + ); + tracing::info!( + "All protocols (TCP/UDP/ICMP) are now allowed on interface: {}", + ifname + ); + } Err(e) => { tracing::warn!("Failed to configure Windows Firewall for {}: {}", ifname, e); - println!("⚠ Warning: Failed to configure Windows Firewall for interface {}.", ifname); + println!( + "⚠ Warning: Failed to configure Windows Firewall for interface {}.", + ifname + ); println!(" This may cause connectivity issues with ping and other network functions."); - println!(" Please run as Administrator or manually configure Windows Firewall."); - println!(" Alternatively, you can disable Windows Firewall for testing purposes."); + println!( + " Please run as Administrator or manually configure Windows Firewall." + ); + println!( + " Alternatively, you can disable Windows Firewall for testing purposes." + ); } } } @@ -615,7 +732,7 @@ impl NicCtx { if payload.is_empty() { return; } - + match payload[0] >> 4 { 4 => Self::do_forward_nic_to_peers_ipv4(ret, mgr).await, 6 => Self::do_forward_nic_to_peers_ipv6(ret, mgr).await, @@ -754,7 +871,11 @@ impl NicCtx { Ok(()) } - pub async fn run(&mut self, ipv4_addr: Option, ipv6_addr: Option) -> Result<(), Error> { + pub async fn run( + &mut self, + ipv4_addr: Option, + ipv6_addr: Option, + ) -> Result<(), Error> { let tunnel = { let mut nic = self.nic.lock().await; match nic.create_dev().await { @@ -795,12 +916,12 @@ impl NicCtx { if let Some(ipv4_addr) = ipv4_addr { self.assign_ipv4_to_tun_device(ipv4_addr).await?; } - + // Assign IPv6 address if provided if let Some(ipv6_addr) = ipv6_addr { self.assign_ipv6_to_tun_device(ipv6_addr).await?; } - + self.run_proxy_cidrs_route_updater().await?; Ok(())