From 27a6c686a7c78f9b375c555379d39cddc926c104 Mon Sep 17 00:00:00 2001 From: lubeilin <1791778603@qq.com> Date: Thu, 29 Feb 2024 22:20:30 +0800 Subject: [PATCH] =?UTF-8?q?[mio]=20=E5=90=88=E5=B9=B6=E5=90=84=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=E7=9A=84tun/tap=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnt/tun/Cargo.toml | 32 ++ vnt/tun/src/android/mod.rs | 56 ++++ vnt/tun/src/device.rs | 24 ++ vnt/tun/src/lib.rs | 31 ++ vnt/tun/src/linux/device.rs | 303 ++++++++++++++++++ vnt/tun/src/linux/mod.rs | 4 + vnt/tun/src/linux/route.rs | 16 + vnt/tun/src/linux/sys.rs | 21 ++ vnt/tun/src/macos/device.rs | 286 +++++++++++++++++ vnt/tun/src/macos/mod.rs | 5 + vnt/tun/src/macos/route.rs | 18 ++ .../src/platform => tun/src}/macos/sys.rs | 33 +- vnt/tun/src/packet/arp/mod.rs | 1 + vnt/tun/src/packet/arp/packet.rs | 122 +++++++ vnt/tun/src/packet/ethernet/mod.rs | 2 + vnt/tun/src/packet/ethernet/packet.rs | 77 +++++ vnt/tun/src/packet/ethernet/protocol.rs | 141 ++++++++ vnt/tun/src/packet/mod.rs | 64 ++++ vnt/tun/src/unix/fd.rs | 62 ++++ vnt/tun/src/unix/mod.rs | 27 ++ .../posix => tun/src/unix}/sockaddr.rs | 39 +-- vnt/tun/src/windows/device.rs | 92 ++++++ .../src => tun/src/windows}/ffi.rs | 4 +- vnt/tun/src/windows/mod.rs | 43 +++ vnt/tun/src/windows/netsh.rs | 47 +++ vnt/tun/src/windows/route.rs | 33 ++ vnt/tun/src/windows/tap/mod.rs | 193 +++++++++++ .../src => tun/src/windows}/tun/mod.rs | 303 ++++++++---------- .../src => tun/src/windows}/tun/packet.rs | 4 +- .../src => tun/src/windows}/tun/wintun_log.rs | 2 +- .../src => tun/src/windows}/tun/wintun_raw.rs | 0 31 files changed, 1858 insertions(+), 227 deletions(-) create mode 100644 vnt/tun/Cargo.toml create mode 100644 vnt/tun/src/android/mod.rs create mode 100644 vnt/tun/src/device.rs create mode 100644 vnt/tun/src/lib.rs create mode 100644 vnt/tun/src/linux/device.rs create mode 100644 vnt/tun/src/linux/mod.rs create mode 100644 vnt/tun/src/linux/route.rs create mode 100644 vnt/tun/src/linux/sys.rs create mode 100644 vnt/tun/src/macos/device.rs create mode 100644 vnt/tun/src/macos/mod.rs create mode 100644 vnt/tun/src/macos/route.rs rename vnt/{rust-tun/src/platform => tun/src}/macos/sys.rs (72%) create mode 100644 vnt/tun/src/packet/arp/mod.rs create mode 100644 vnt/tun/src/packet/arp/packet.rs create mode 100644 vnt/tun/src/packet/ethernet/mod.rs create mode 100644 vnt/tun/src/packet/ethernet/packet.rs create mode 100644 vnt/tun/src/packet/ethernet/protocol.rs create mode 100644 vnt/tun/src/packet/mod.rs create mode 100644 vnt/tun/src/unix/fd.rs create mode 100644 vnt/tun/src/unix/mod.rs rename vnt/{rust-tun/src/platform/posix => tun/src/unix}/sockaddr.rs (57%) create mode 100644 vnt/tun/src/windows/device.rs rename vnt/{win-tun-tap/src => tun/src/windows}/ffi.rs (98%) create mode 100644 vnt/tun/src/windows/mod.rs create mode 100644 vnt/tun/src/windows/netsh.rs create mode 100644 vnt/tun/src/windows/route.rs create mode 100644 vnt/tun/src/windows/tap/mod.rs rename vnt/{win-tun-tap/src => tun/src/windows}/tun/mod.rs (52%) rename vnt/{win-tun-tap/src => tun/src/windows}/tun/packet.rs (96%) rename vnt/{win-tun-tap/src => tun/src/windows}/tun/wintun_log.rs (97%) rename vnt/{win-tun-tap/src => tun/src/windows}/tun/wintun_raw.rs (100%) diff --git a/vnt/tun/Cargo.toml b/vnt/tun/Cargo.toml new file mode 100644 index 0000000..cdede0f --- /dev/null +++ b/vnt/tun/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "tun" +version = "0.1.0" +edition = "2021" + + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = "0.2.153" + +log = { version = "0.4.20", features = [] } +rand = "0.8.5" + +[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] +ioctl = { version = "0.8", package = "ioctl-sys" } + +[target.'cfg(target_os = "windows")'.dependencies] +libloading = "0.8.0" +widestring = "1.0.2" +winapi = {version = "0.3",features = [ + "errhandlingapi", + "combaseapi", + "ioapiset", + "winioctl", + "setupapi", + "synchapi", + "netioapi", + "fileapi","handleapi","winerror","minwindef","ifdef","basetsd","winnt","winreg","winbase","minwinbase", + "impl-default" +]} + diff --git a/vnt/tun/src/android/mod.rs b/vnt/tun/src/android/mod.rs new file mode 100644 index 0000000..d1d3f1c --- /dev/null +++ b/vnt/tun/src/android/mod.rs @@ -0,0 +1,56 @@ +use crate::device::IFace; +use crate::Fd; +use std::io; +use std::net::Ipv4Addr; +use std::os::fd::RawFd; + +pub struct Device { + fd: Fd, +} + +impl Device { + pub fn new(fd: RawFd) -> io::Result { + Ok(Self { fd: Fd::new(fd)? }) + } +} +impl IFace for Device { + fn version(&self) -> io::Result { + Ok(String::new()) + } + + fn name(&self) -> io::Result { + Ok(String::new()) + } + + fn shutdown(&self) -> io::Result<()> { + Err(io::Error::from(io::ErrorKind::Unsupported)) + } + + fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()> { + Err(io::Error::from(io::ErrorKind::Unsupported)) + } + + fn mtu(&self) -> io::Result { + Err(io::Error::from(io::ErrorKind::Unsupported)) + } + + fn set_mtu(&self, value: u32) -> io::Result<()> { + Err(io::Error::from(io::ErrorKind::Unsupported)) + } + + fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, metric: u16) -> io::Result<()> { + Err(io::Error::from(io::ErrorKind::Unsupported)) + } + + fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> { + Err(io::Error::from(io::ErrorKind::Unsupported)) + } + + fn read(&self, buf: &mut [u8]) -> io::Result { + self.fd.read(buf) + } + + fn write(&self, buf: &[u8]) -> io::Result { + self.fd.write(buf) + } +} diff --git a/vnt/tun/src/device.rs b/vnt/tun/src/device.rs new file mode 100644 index 0000000..d1a71b3 --- /dev/null +++ b/vnt/tun/src/device.rs @@ -0,0 +1,24 @@ +use io::Result; +use std::io; +use std::net::Ipv4Addr; + +pub trait IFace { + fn version(&self) -> Result; + /// Get the device name. + fn name(&self) -> Result; + + fn shutdown(&self) -> Result<()>; + + fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> Result<()>; + + /// Get the MTU. + fn mtu(&self) -> Result; + + /// Set the MTU. + fn set_mtu(&self, value: u32) -> Result<()>; + fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, metric: u16) -> Result<()>; + fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> Result<()>; + + fn read(&self, buf: &mut [u8]) -> Result; + fn write(&self, buf: &[u8]) -> Result; +} diff --git a/vnt/tun/src/lib.rs b/vnt/tun/src/lib.rs new file mode 100644 index 0000000..d287dd9 --- /dev/null +++ b/vnt/tun/src/lib.rs @@ -0,0 +1,31 @@ +/// 参考 +/// https://github.com/meh/rust-tun +/// https://github.com/Tazdevil971/tap-windows +/// https://github.com/nulldotblack/wintun +pub mod device; +mod packet; + +#[cfg(target_os = "linux")] +mod linux; +#[cfg(target_os = "linux")] +pub use linux::Device; + +#[cfg(target_os = "android")] +mod android; +#[cfg(target_os = "android")] +pub use android::Device; + +#[cfg(target_os = "macos")] +mod macos; +#[cfg(target_os = "macos")] +pub use macos::Device; + +#[cfg(unix)] +mod unix; +#[cfg(unix)] +pub use unix::Fd; +#[cfg(windows)] +mod windows; + +#[cfg(windows)] +pub use windows::Device; diff --git a/vnt/tun/src/linux/device.rs b/vnt/tun/src/linux/device.rs new file mode 100644 index 0000000..25568d8 --- /dev/null +++ b/vnt/tun/src/linux/device.rs @@ -0,0 +1,303 @@ +use std::ffi::{CStr, CString}; +use std::net::Ipv4Addr; +use std::os::fd::AsRawFd; +use std::process::Command; +use std::{io, mem, ptr}; + +use libc::{ + c_char, c_short, ifreq, AF_INET, IFF_MULTI_QUEUE, IFF_NO_PI, IFF_RUNNING, IFF_TAP, IFF_TUN, + IFF_UP, IFNAMSIZ, O_RDWR, SOCK_DGRAM, +}; + +use crate::device::IFace; +use crate::linux::route; +use crate::linux::sys::*; +use crate::packet; +use crate::unix::{exe_cmd, Fd, SockAddr}; + +pub struct Device { + name: String, + ctl: Fd, + tun: Fd, + mac: Option<[u8; 6]>, +} + +impl Device { + pub fn new(name: Option<&str>, tap: bool) -> io::Result { + let device = unsafe { + let dev = match name { + Some(name) => { + let name = + CString::new(name).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + + if name.as_bytes_with_nul().len() > IFNAMSIZ { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "name too long")); + } + + Some(name) + } + + None => None, + }; + + let mut req: ifreq = mem::zeroed(); + + if let Some(dev) = dev.as_ref() { + ptr::copy_nonoverlapping( + dev.as_ptr() as *const c_char, + req.ifr_name.as_mut_ptr(), + dev.as_bytes().len(), + ); + } + + let device_type: c_short = if tap { IFF_TAP } else { IFF_TUN } as c_short; + + let queues_num = 1; + + let iff_no_pi = IFF_NO_PI as c_short; + let iff_multi_queue = IFF_MULTI_QUEUE as c_short; + let packet_information = false; + req.ifr_ifru.ifru_flags = device_type + | if packet_information { 0 } else { iff_no_pi } + | if queues_num > 1 { iff_multi_queue } else { 0 }; + + let tun = Fd::new(libc::open(b"/dev/net/tun\0".as_ptr() as *const _, O_RDWR)) + .map_err(|_| io::Error::last_os_error())?; + + if tunsetiff(tun.0, &mut req as *mut _ as *mut _) < 0 { + return Err(io::Error::last_os_error()); + } + + let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))?; + + let name = CStr::from_ptr(req.ifr_name.as_ptr()) + .to_string_lossy() + .to_string(); + let mac = if tap { + let get_mac_cmd = format!("cat /sys/class/net/{}/address", name); + let mac_out = exe_cmd(&get_mac_cmd)?; + let mac_str = String::from_utf8(mac_out.stdout).unwrap(); + let mut mac = [0; 6]; + let mut split = mac_str.split(":"); + for i in 0..6 { + mac[i] = u8::from_str_radix(&split.next().unwrap()[..2], 16).unwrap(); + } + Some(mac) + } else { + None + }; + Device { + name, + tun, + ctl, + mac, + } + }; + device.enabled(true)?; + Ok(device) + } +} + +impl Device { + fn enabled(&self, value: bool) -> io::Result<()> { + unsafe { + let mut req = self.request(); + + if siocgifflags(self.ctl.as_raw_fd(), &mut req) < 0 { + return Err(io::Error::last_os_error()); + } + + if value { + req.ifr_ifru.ifru_flags |= (IFF_UP | IFF_RUNNING) as c_short; + } else { + req.ifr_ifru.ifru_flags &= !(IFF_UP as c_short); + } + + if siocsifflags(self.ctl.as_raw_fd(), &req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + } + unsafe fn request(&self) -> ifreq { + let mut req: ifreq = mem::zeroed(); + ptr::copy_nonoverlapping( + self.name.as_ptr() as *const c_char, + req.ifr_name.as_mut_ptr(), + self.name.len(), + ); + req + } + fn address(&self) -> io::Result { + unsafe { + let mut req = self.request(); + + if siocgifaddr(self.ctl.as_raw_fd(), &mut req) < 0 { + return Err(io::Error::last_os_error().into()); + } + + SockAddr::new(&req.ifr_ifru.ifru_addr).map(Into::into) + } + } + + fn set_address(&self, value: Ipv4Addr) -> io::Result<()> { + unsafe { + let mut req = self.request(); + req.ifr_ifru.ifru_addr = SockAddr::from(value).into(); + + if siocsifaddr(self.ctl.as_raw_fd(), &req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + } + + fn destination(&self) -> io::Result { + unsafe { + let mut req = self.request(); + + if siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) < 0 { + return Err(io::Error::last_os_error()); + } + + SockAddr::new(&req.ifr_ifru.ifru_dstaddr).map(Into::into) + } + } + + fn set_destination(&self, value: Ipv4Addr) -> io::Result<()> { + unsafe { + let mut req = self.request(); + req.ifr_ifru.ifru_dstaddr = SockAddr::from(value).into(); + + if siocsifdstaddr(self.ctl.as_raw_fd(), &req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + } + + fn broadcast(&self) -> io::Result { + unsafe { + let mut req = self.request(); + + if siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) < 0 { + return Err(io::Error::last_os_error()); + } + + SockAddr::new(&req.ifr_ifru.ifru_broadaddr).map(Into::into) + } + } + + fn set_broadcast(&self, value: Ipv4Addr) -> io::Result<()> { + unsafe { + let mut req = self.request(); + req.ifr_ifru.ifru_broadaddr = SockAddr::from(value).into(); + + if siocsifbrdaddr(self.ctl.as_raw_fd(), &req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + } + + fn netmask(&self) -> io::Result { + unsafe { + let mut req = self.request(); + + if siocgifnetmask(self.ctl.as_raw_fd(), &mut req) < 0 { + return Err(io::Error::last_os_error()); + } + + SockAddr::new(&req.ifr_ifru.ifru_netmask).map(Into::into) + } + } + + fn set_netmask(&self, value: Ipv4Addr) -> io::Result<()> { + unsafe { + let mut req = self.request(); + req.ifr_ifru.ifru_netmask = SockAddr::from(value).into(); + + if siocsifnetmask(self.ctl.as_raw_fd(), &req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + } +} + +impl IFace for Device { + fn version(&self) -> io::Result { + Ok(String::new()) + } + + fn name(&self) -> io::Result { + Ok(self.name.clone()) + } + + fn shutdown(&self) -> io::Result<()> { + exe_cmd(&format!("ip link delete {}", self.name))?; + Ok(()) + } + fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()> { + self.set_address(address)?; + self.set_netmask(mask) + } + + fn mtu(&self) -> io::Result { + unsafe { + let mut req = self.request(); + + if siocgifmtu(self.ctl.as_raw_fd(), &mut req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(req.ifr_ifru.ifru_mtu as u32) + } + } + + fn set_mtu(&self, value: u32) -> io::Result<()> { + unsafe { + let mut req = self.request(); + req.ifr_ifru.ifru_mtu = value as _; + + if siocsifmtu(self.ctl.as_raw_fd(), &req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + } + + fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, _metric: u16) -> io::Result<()> { + route::add_route(&self.name, dest, netmask) + } + + fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> { + route::del_route(&self.name, dest, netmask) + } + + fn read(&self, buf: &mut [u8]) -> io::Result { + if self.mac.is_some() { + packet::read_tap( + buf, + |eth_buf| self.tun.read(eth_buf), + |eth_buf| self.tun.write(eth_buf), + ) + } else { + self.tun.read(buf) + } + } + + fn write(&self, buf: &[u8]) -> io::Result { + if let Some(mac) = &self.mac { + packet::write_tap(buf, |eth_buf| self.tun.write(eth_buf), mac) + } else { + self.tun.write(buf) + } + } +} diff --git a/vnt/tun/src/linux/mod.rs b/vnt/tun/src/linux/mod.rs new file mode 100644 index 0000000..8e8dcc6 --- /dev/null +++ b/vnt/tun/src/linux/mod.rs @@ -0,0 +1,4 @@ +mod device; +pub use device::Device; +mod route; +mod sys; diff --git a/vnt/tun/src/linux/route.rs b/vnt/tun/src/linux/route.rs new file mode 100644 index 0000000..b50133c --- /dev/null +++ b/vnt/tun/src/linux/route.rs @@ -0,0 +1,16 @@ +use std::io; +use std::net::Ipv4Addr; + +use crate::unix::exe_cmd; + +pub fn add_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> { + let cmd = format!("ip route add {:?}/{:?} dev {}", address, netmask, name); + exe_cmd(&cmd)?; + Ok(()) +} + +pub fn del_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> { + let cmd = format!("ip route del {:?}/{:?} dev {}", address, netmask, name); + exe_cmd(&cmd)?; + Ok(()) +} diff --git a/vnt/tun/src/linux/sys.rs b/vnt/tun/src/linux/sys.rs new file mode 100644 index 0000000..0913dda --- /dev/null +++ b/vnt/tun/src/linux/sys.rs @@ -0,0 +1,21 @@ +use ioctl::*; +use libc::{c_int, ifreq}; + +ioctl!(bad read siocgifflags with 0x8913; ifreq); +ioctl!(bad write siocsifflags with 0x8914; ifreq); +ioctl!(bad read siocgifaddr with 0x8915; ifreq); +ioctl!(bad write siocsifaddr with 0x8916; ifreq); +ioctl!(bad read siocgifdstaddr with 0x8917; ifreq); +ioctl!(bad write siocsifdstaddr with 0x8918; ifreq); +ioctl!(bad read siocgifbrdaddr with 0x8919; ifreq); +ioctl!(bad write siocsifbrdaddr with 0x891a; ifreq); +ioctl!(bad read siocgifnetmask with 0x891b; ifreq); +ioctl!(bad write siocsifnetmask with 0x891c; ifreq); +ioctl!(bad read siocgifmtu with 0x8921; ifreq); +ioctl!(bad write siocsifmtu with 0x8922; ifreq); +ioctl!(bad write siocsifname with 0x8923; ifreq); + +ioctl!(write tunsetiff with b'T', 202; c_int); +ioctl!(write tunsetpersist with b'T', 203; c_int); +ioctl!(write tunsetowner with b'T', 204; c_int); +ioctl!(write tunsetgroup with b'T', 206; c_int); diff --git a/vnt/tun/src/macos/device.rs b/vnt/tun/src/macos/device.rs new file mode 100644 index 0000000..eb7ef9f --- /dev/null +++ b/vnt/tun/src/macos/device.rs @@ -0,0 +1,286 @@ +use std::ffi::{c_void, CStr}; +use std::net::Ipv4Addr; +use std::os::fd::AsRawFd; +use std::{io, mem, ptr}; + +use libc::{ + c_char, c_short, c_uint, sockaddr, socklen_t, AF_INET, AF_SYSTEM, AF_SYS_CONTROL, IFF_RUNNING, + IFF_UP, IFNAMSIZ, PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, +}; + +use crate::device::IFace; +use crate::macos::route; +use crate::macos::sys::*; +use crate::unix::{Fd, SockAddr}; + +pub struct Device { + name: String, + ctl: Fd, + tun: Fd, +} + +impl Device { + pub fn new(name: Option<&str>) -> io::Result { + let id = if let Some(name) = name { + if name.len() > IFNAMSIZ { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "name too long")); + } + + if !name.starts_with("utun") { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid name")); + } + + name[4..] + .parse::() + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))? + + 1u32 + } else { + 0u32 + }; + let device = unsafe { + let tun = Fd::new(libc::socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL))?; + + let mut info = ctl_info { + ctl_id: 0, + ctl_name: { + let mut buffer = [0; 96]; + for (i, o) in UTUN_CONTROL_NAME.as_bytes().iter().zip(buffer.iter_mut()) { + *o = *i as _; + } + buffer + }, + }; + + if ctliocginfo(tun.0, &mut info as *mut _ as *mut _) < 0 { + return Err(io::Error::last_os_error()); + } + + let addr = sockaddr_ctl { + sc_id: info.ctl_id, + sc_len: mem::size_of::() as _, + sc_family: AF_SYSTEM as _, + ss_sysaddr: AF_SYS_CONTROL as _, + sc_unit: id as c_uint, + sc_reserved: [0; 5], + }; + + let address = &addr as *const sockaddr_ctl as *const sockaddr; + if libc::connect(tun.0, address, mem::size_of_val(&addr) as socklen_t) < 0 { + return Err(io::Error::last_os_error()); + } + + let mut name = [0u8; 64]; + let mut name_len: socklen_t = 64; + + let optval = &mut name as *mut _ as *mut c_void; + let optlen = &mut name_len as *mut socklen_t; + if libc::getsockopt(tun.0, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, optval, optlen) < 0 { + return Err(io::Error::last_os_error()); + } + + let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))?; + + Device { + name: CStr::from_ptr(name.as_ptr() as *const c_char) + .to_string_lossy() + .into(), + tun, + ctl, + } + }; + device.enabled(true)?; + Ok(device) + } +} + +impl Device { + fn enabled(&self, value: bool) -> io::Result<()> { + unsafe { + let mut req = self.request(); + + if siocgifflags(self.ctl.as_raw_fd(), &mut req) < 0 { + return Err(io::Error::last_os_error()); + } + + if value { + req.ifru.flags |= (IFF_UP | IFF_RUNNING) as c_short; + } else { + req.ifru.flags &= !(IFF_UP as c_short); + } + + if siocsifflags(self.ctl.as_raw_fd(), &req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + } + unsafe fn request(&self) -> ifreq { + let mut req: ifreq = mem::zeroed(); + ptr::copy_nonoverlapping( + self.name.as_ptr() as *const c_char, + req.ifrn.name.as_mut_ptr(), + self.name.len(), + ); + req + } + fn address(&self) -> io::Result { + unsafe { + let mut req = self.request(); + + if siocgifaddr(self.ctl.as_raw_fd(), &mut req) < 0 { + return Err(io::Error::last_os_error()); + } + + SockAddr::new(&req.ifru.addr).map(Into::into) + } + } + + fn set_address(&self, value: Ipv4Addr) -> io::Result<()> { + unsafe { + let mut req = self.request(); + req.ifru.addr = SockAddr::from(value).into(); + + if siocsifaddr(self.ctl.as_raw_fd(), &req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + } + + fn destination(&self) -> io::Result { + unsafe { + let mut req = self.request(); + + if siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) < 0 { + return Err(io::Error::last_os_error()); + } + + SockAddr::new(&req.ifru.dstaddr).map(Into::into) + } + } + + fn set_destination(&self, value: Ipv4Addr) -> io::Result<()> { + unsafe { + let mut req = self.request(); + req.ifru.dstaddr = SockAddr::from(value).into(); + + if siocsifdstaddr(self.ctl.as_raw_fd(), &req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + } + + fn broadcast(&self) -> io::Result { + unsafe { + let mut req = self.request(); + + if siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) < 0 { + return Err(io::Error::last_os_error()); + } + + SockAddr::new(&req.ifru.broadaddr).map(Into::into) + } + } + + fn set_broadcast(&self, value: Ipv4Addr) -> io::Result<()> { + unsafe { + let mut req = self.request(); + req.ifru.broadaddr = SockAddr::from(value).into(); + + if siocsifbrdaddr(self.ctl.as_raw_fd(), &req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + } + + fn netmask(&self) -> io::Result { + unsafe { + let mut req = self.request(); + + if siocgifnetmask(self.ctl.as_raw_fd(), &mut req) < 0 { + return Err(io::Error::last_os_error()); + } + + SockAddr::unchecked(&req.ifru.addr).map(Into::into) + } + } + + fn set_netmask(&self, value: Ipv4Addr) -> io::Result<()> { + unsafe { + let mut req = self.request(); + req.ifru.addr = SockAddr::from(value).into(); + + if siocsifnetmask(self.ctl.as_raw_fd(), &req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + } +} + +impl IFace for Device { + fn version(&self) -> io::Result { + Ok(String::new()) + } + + fn name(&self) -> io::Result { + Ok(self.name.clone()) + } + + fn shutdown(&self) -> io::Result<()> { + self.enabled(false) + } + + fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()> { + self.set_address(address)?; + self.set_netmask(mask) + } + + fn mtu(&self) -> io::Result { + unsafe { + let mut req = self.request(); + + if siocgifmtu(self.ctl.as_raw_fd(), &mut req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(req.ifru.mtu as _) + } + } + + fn set_mtu(&self, value: u32) -> io::Result<()> { + unsafe { + let mut req = self.request(); + req.ifru.mtu = value as _; + + if siocsifmtu(self.ctl.as_raw_fd(), &req) < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + } + + fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, _metric: u16) -> io::Result<()> { + route::add_route(&self.name, dest, netmask) + } + + fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> { + route::del_route(&self.name, dest, netmask) + } + + fn read(&self, buf: &mut [u8]) -> io::Result { + self.tun.read(buf) + } + + fn write(&self, buf: &[u8]) -> io::Result { + self.tun.write(buf) + } +} diff --git a/vnt/tun/src/macos/mod.rs b/vnt/tun/src/macos/mod.rs new file mode 100644 index 0000000..2a7a0fe --- /dev/null +++ b/vnt/tun/src/macos/mod.rs @@ -0,0 +1,5 @@ +mod device; +pub use device::Device; +mod sys; + +mod route; diff --git a/vnt/tun/src/macos/route.rs b/vnt/tun/src/macos/route.rs new file mode 100644 index 0000000..baab9a7 --- /dev/null +++ b/vnt/tun/src/macos/route.rs @@ -0,0 +1,18 @@ +use crate::unix::exe_cmd; +use std::io; +use std::net::Ipv4Addr; + +pub fn add_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> { + let cmd = format!( + "route -n add {} -netmask {} -interface {}", + address, netmask, name + ); + exe_cmd(&cmd) +} +pub fn del_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> { + let cmd = format!( + "route -n delete {} -netmask {} -interface {}", + address, netmask, name + ); + exe_cmd(&cmd) +} diff --git a/vnt/rust-tun/src/platform/macos/sys.rs b/vnt/tun/src/macos/sys.rs similarity index 72% rename from vnt/rust-tun/src/platform/macos/sys.rs rename to vnt/tun/src/macos/sys.rs index 0a9ff2f..ae42318 100644 --- a/vnt/rust-tun/src/platform/macos/sys.rs +++ b/vnt/tun/src/macos/sys.rs @@ -1,35 +1,11 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - //! Bindings to internal macOS stuff. use ioctl::*; -use libc::sockaddr; -use libc::{c_char, c_int, c_short, c_uint, c_ushort, c_void}; +use libc::{c_char, c_int, c_short, c_uint, c_ushort, c_void, sockaddr, IFNAMSIZ}; -pub const IFNAMSIZ: usize = 16; - -pub const IFF_UP: c_short = 0x1; -pub const IFF_RUNNING: c_short = 0x40; - -pub const AF_SYS_CONTROL: c_ushort = 2; -pub const AF_SYSTEM: c_char = 32; -pub const PF_SYSTEM: c_int = AF_SYSTEM as c_int; -pub const SYSPROTO_CONTROL: c_int = 2; -pub const UTUN_OPT_IFNAME: c_int = 2; pub const UTUN_CONTROL_NAME: &str = "com.apple.net.utun_control"; +#[allow(non_camel_case_types)] #[repr(C)] #[derive(Copy, Clone)] pub struct ctl_info { @@ -37,6 +13,7 @@ pub struct ctl_info { pub ctl_name: [c_char; 96], } +#[allow(non_camel_case_types)] #[repr(C)] #[derive(Copy, Clone)] pub struct sockaddr_ctl { @@ -54,6 +31,7 @@ pub union ifrn { pub name: [c_char; IFNAMSIZ], } +#[allow(non_camel_case_types)] #[repr(C)] #[derive(Copy, Clone)] pub struct ifdevmtu { @@ -69,6 +47,7 @@ pub union ifku { pub value: c_int, } +#[allow(non_camel_case_types)] #[repr(C)] #[derive(Copy, Clone)] pub struct ifkpi { @@ -98,6 +77,7 @@ pub union ifru { pub functional_type: c_uint, } +#[allow(non_camel_case_types)] #[repr(C)] #[derive(Copy, Clone)] pub struct ifreq { @@ -105,6 +85,7 @@ pub struct ifreq { pub ifru: ifru, } +#[allow(non_camel_case_types)] #[repr(C)] #[derive(Copy, Clone)] pub struct ifaliasreq { diff --git a/vnt/tun/src/packet/arp/mod.rs b/vnt/tun/src/packet/arp/mod.rs new file mode 100644 index 0000000..edb43db --- /dev/null +++ b/vnt/tun/src/packet/arp/mod.rs @@ -0,0 +1 @@ +pub mod packet; diff --git a/vnt/tun/src/packet/arp/packet.rs b/vnt/tun/src/packet/arp/packet.rs new file mode 100644 index 0000000..ea027a3 --- /dev/null +++ b/vnt/tun/src/packet/arp/packet.rs @@ -0,0 +1,122 @@ +use std::{fmt, io}; + +/// 地址解析协议,由IP地址找到MAC地址 +/// https://www.ietf.org/rfc/rfc6747.txt +/* + 0 2 4 5 6 8 10 (字节) + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 硬件类型|协议类型|硬件地址长度|协议地址长度|操作类型| + | 源MAC地址 | 源ip地址 | + | 目的MAC地址 | 目的ip地址 | +*/ + +pub struct ArpPacket { + buffer: B, +} + +impl> ArpPacket { + pub fn unchecked(buffer: B) -> Self { + Self { buffer } + } + pub fn new(buffer: B) -> io::Result { + if buffer.as_ref().len() != 28 { + Err(io::Error::from(io::ErrorKind::InvalidData))?; + } + let packet = Self::unchecked(buffer); + Ok(packet) + } +} + +impl> ArpPacket { + /// 硬件类型 以太网类型为1 + pub fn hardware_type(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap()) + } + /// 上层协议类型,ipv4是0x0800 + pub fn protocol_type(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) + } + /// 如果是MAC地址 则长度为6 + pub fn hardware_size(&self) -> u8 { + self.buffer.as_ref()[4] + } + /// 如果是IPv4 则长度为4 + pub fn protocol_size(&self) -> u8 { + self.buffer.as_ref()[5] + } + /// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3:RARP请求,4:RARP响应 + pub fn op_code(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap()) + } + /// 发送端硬件地址,仅支持以太网 + pub fn sender_hardware_addr(&self) -> &[u8] { + &self.buffer.as_ref()[8..14] + } + /// 发送端协议地址,仅支持IPv4 + pub fn sender_protocol_addr(&self) -> &[u8] { + &self.buffer.as_ref()[14..18] + } + /// 接收端硬件地址,仅支持以太网 + pub fn target_hardware_addr(&self) -> &[u8] { + &self.buffer.as_ref()[18..24] + } + /// 接收端协议地址,仅支持IPv4 + pub fn target_protocol_addr(&self) -> &[u8] { + &self.buffer.as_ref()[24..28] + } +} + +impl + AsMut<[u8]>> ArpPacket { + /// 硬件类型 以太网类型为1 + pub fn set_hardware_type(&mut self, value: u16) { + self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes()) + } + /// 上层协议类型,ipv4是0x0800 + pub fn set_protocol_type(&mut self, value: u16) { + self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes()) + } + /// 如果是MAC地址 则长度为6 + pub fn set_hardware_size(&mut self, value: u8) { + self.buffer.as_mut()[4] = value + } + /// 如果是IPv4 则长度为4 + pub fn set_protocol_size(&mut self, value: u8) { + self.buffer.as_mut()[5] = value + } + /// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3:RARP请求,4:RARP响应 + pub fn set_op_code(&mut self, value: u16) { + self.buffer.as_mut()[6..8].copy_from_slice(&value.to_be_bytes()) + } + /// 发送端硬件地址,仅支持以太网 + pub fn set_sender_hardware_addr(&mut self, buf: &[u8]) { + self.buffer.as_mut()[8..14].copy_from_slice(buf) + } + /// 发送端协议地址,仅支持IPv4 + pub fn set_sender_protocol_addr(&mut self, buf: &[u8]) { + self.buffer.as_mut()[14..18].copy_from_slice(buf) + } + /// 接收端硬件地址,仅支持以太网 + pub fn set_target_hardware_addr(&mut self, buf: &[u8]) { + self.buffer.as_mut()[18..24].copy_from_slice(buf) + } + /// 接收端协议地址,仅支持IPv4 + pub fn set_target_protocol_addr(&mut self, buf: &[u8]) { + self.buffer.as_mut()[24..28].copy_from_slice(buf) + } +} + +impl> fmt::Debug for ArpPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ArpPacket") + .field("hardware_type", &self.hardware_type()) + .field("protocol_type", &self.protocol_type()) + .field("hardware_size", &self.hardware_size()) + .field("protocol_size", &self.protocol_size()) + .field("op_code", &self.op_code()) + .field("sender_hardware_addr", &self.sender_hardware_addr()) + .field("sender_protocol_addr", &self.sender_protocol_addr()) + .field("target_hardware_addr", &self.target_hardware_addr()) + .field("target_protocol_addr", &self.target_protocol_addr()) + .finish() + } +} diff --git a/vnt/tun/src/packet/ethernet/mod.rs b/vnt/tun/src/packet/ethernet/mod.rs new file mode 100644 index 0000000..c9cf115 --- /dev/null +++ b/vnt/tun/src/packet/ethernet/mod.rs @@ -0,0 +1,2 @@ +pub mod packet; +pub mod protocol; diff --git a/vnt/tun/src/packet/ethernet/packet.rs b/vnt/tun/src/packet/ethernet/packet.rs new file mode 100644 index 0000000..a9540a7 --- /dev/null +++ b/vnt/tun/src/packet/ethernet/packet.rs @@ -0,0 +1,77 @@ +use crate::packet::ethernet::protocol::Protocol; +use std::{fmt, io}; + +/// 以太网帧协议 +/// https://www.ietf.org/rfc/rfc894.txt +/* + 0 6 12 14 (字节) + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 目的地址 | 源地址 | 类型 | +*/ +pub struct EthernetPacket { + pub buffer: B, +} + +impl> EthernetPacket { + pub fn unchecked(buffer: B) -> EthernetPacket { + EthernetPacket { buffer } + } + + pub fn new(buffer: B) -> io::Result> { + let packet = EthernetPacket::unchecked(buffer); + //头部固定14位 + if packet.buffer.as_ref().len() < 14 { + Err(io::Error::from(io::ErrorKind::InvalidData))?; + } + + Ok(packet) + } +} + +impl> EthernetPacket { + /// 目的MAC地址 + pub fn destination(&self) -> &[u8] { + &self.buffer.as_ref()[0..6] + } + /// 源MAC地址 + pub fn source(&self) -> &[u8] { + &self.buffer.as_ref()[6..12] + } + /// 3层协议 + pub fn protocol(&self) -> Protocol { + u16::from_be_bytes(self.buffer.as_ref()[12..14].try_into().unwrap()).into() + } + /// 载荷 + pub fn payload(&self) -> &[u8] { + &self.buffer.as_ref()[14..] + } +} + +impl + AsMut<[u8]>> EthernetPacket { + pub fn set_destination(&mut self, value: &[u8]) { + self.buffer.as_mut()[0..6].copy_from_slice(value); + } + + pub fn set_source(&mut self, value: &[u8]) { + self.buffer.as_mut()[6..12].copy_from_slice(value); + } + + pub fn set_protocol(&mut self, value: Protocol) { + let p: u16 = value.into(); + self.buffer.as_mut()[12..14].copy_from_slice(&p.to_be_bytes()) + } + pub fn payload_mut(&mut self) -> &mut [u8] { + &mut self.buffer.as_mut()[14..] + } +} + +impl> fmt::Debug for EthernetPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("EthernetPacket") + .field("destination", &self.destination()) + .field("source", &self.source()) + .field("protocol", &self.protocol()) + .field("payload", &self.payload()) + .finish() + } +} diff --git a/vnt/tun/src/packet/ethernet/protocol.rs b/vnt/tun/src/packet/ethernet/protocol.rs new file mode 100644 index 0000000..e775a28 --- /dev/null +++ b/vnt/tun/src/packet/ethernet/protocol.rs @@ -0,0 +1,141 @@ +/// 以太网帧协议 +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum Protocol { + /// + Ipv4, + + /// + Arp, + + /// + WakeOnLan, + + /// + Trill, + + /// + DecNet, + + /// + Rarp, + + /// + AppleTalk, + + /// + Aarp, + + /// + Ipx, + + /// + Qnx, + + /// + Ipv6, + + /// + FlowControl, + + /// + CobraNet, + + /// + Mpls, + + /// + MplsMulticast, + + /// + PppoeDiscovery, + + /// + PppoeSession, + + /// + Vlan, + + /// + PBridge, + + /// + Lldp, + + /// + Ptp, + + /// + Cfm, + + /// + QinQ, + + /// + Unknown(u16), +} + +impl From for Protocol { + fn from(value: u16) -> Protocol { + use self::Protocol::*; + + match value { + 0x0800 => Ipv4, + 0x0806 => Arp, + 0x0842 => WakeOnLan, + 0x22f3 => Trill, + 0x6003 => DecNet, + 0x8035 => Rarp, + 0x809b => AppleTalk, + 0x80f3 => Aarp, + 0x8137 => Ipx, + 0x8204 => Qnx, + 0x86dd => Ipv6, + 0x8808 => FlowControl, + 0x8819 => CobraNet, + 0x8847 => Mpls, + 0x8848 => MplsMulticast, + 0x8863 => PppoeDiscovery, + 0x8864 => PppoeSession, + 0x8100 => Vlan, + 0x88a8 => PBridge, + 0x88cc => Lldp, + 0x88f7 => Ptp, + 0x8902 => Cfm, + 0x9100 => QinQ, + n => Unknown(n), + } + } +} + +impl Into for Protocol { + fn into(self) -> u16 { + use self::Protocol::*; + + match self { + Ipv4 => 0x0800, + Arp => 0x0806, + WakeOnLan => 0x0842, + Trill => 0x22f3, + DecNet => 0x6003, + Rarp => 0x8035, + AppleTalk => 0x809b, + Aarp => 0x80f3, + Ipx => 0x8137, + Qnx => 0x8204, + Ipv6 => 0x86dd, + FlowControl => 0x8808, + CobraNet => 0x8819, + Mpls => 0x8847, + MplsMulticast => 0x8848, + PppoeDiscovery => 0x8863, + PppoeSession => 0x8864, + Vlan => 0x8100, + PBridge => 0x88a8, + Lldp => 0x88cc, + Ptp => 0x88f7, + Cfm => 0x8902, + QinQ => 0x9100, + Unknown(n) => n, + } + } +} diff --git a/vnt/tun/src/packet/mod.rs b/vnt/tun/src/packet/mod.rs new file mode 100644 index 0000000..75d9f31 --- /dev/null +++ b/vnt/tun/src/packet/mod.rs @@ -0,0 +1,64 @@ +use crate::packet::ethernet::protocol::Protocol; +use std::io; + +pub mod arp; +pub mod ethernet; + +const MAC: [u8; 6] = [0xf, 0xf, 0xf, 0xf, 0xe, 0x9]; +pub fn read_tap(buf: &mut [u8], read_fn: R, write_fn: W) -> io::Result +where + W: Fn(&[u8]) -> io::Result, + R: Fn(&mut [u8]) -> io::Result, +{ + let mut eth_buf = [0; 65536]; + loop { + let len = read_fn(&mut eth_buf)?; + //处理arp包 + let mut ether = ethernet::packet::EthernetPacket::unchecked(&mut eth_buf[..len]); + match ether.protocol() { + Protocol::Ipv4 => { + let len = ether.payload().len(); + if len > buf.len() { + return Err(io::Error::new(io::ErrorKind::Other, "short")); + } + buf[..len].copy_from_slice(ether.payload()); + return Ok(len); + } + Protocol::Arp => { + let mut arp_packet = arp::packet::ArpPacket::unchecked(ether.payload_mut()); + let sender_h: [u8; 6] = arp_packet.sender_hardware_addr().try_into().unwrap(); + let sender_p: [u8; 4] = arp_packet.sender_protocol_addr().try_into().unwrap(); + let target_p: [u8; 4] = arp_packet.target_protocol_addr().try_into().unwrap(); + if target_p == [0, 0, 0, 0] || sender_p == [0, 0, 0, 0] || target_p == sender_p { + continue; + } + if arp_packet.op_code() == 1 { + //回复一个默认的MAC + arp_packet.set_op_code(2); + arp_packet.set_target_hardware_addr(&sender_h); + arp_packet.set_target_protocol_addr(&sender_p); + arp_packet.set_sender_protocol_addr(&target_p); + arp_packet.set_sender_hardware_addr(&MAC); + ether.set_destination(&sender_h); + ether.set_source(&MAC); + write_fn(ether.buffer)?; + } + } + _ => { + //忽略这些数据 + } + } + } +} +pub fn write_tap(buf: &[u8], write_fn: W, mac: &[u8; 6]) -> io::Result +where + W: Fn(&[u8]) -> io::Result, +{ + // 封装二层数据 + let mut ether = ethernet::packet::EthernetPacket::unchecked(vec![0; 14 + buf.len()]); + ether.set_source(&MAC); + ether.set_destination(mac); + ether.set_protocol(Protocol::Ipv4); + ether.payload_mut().copy_from_slice(buf); + write_fn(ðer.buffer) +} diff --git a/vnt/tun/src/unix/fd.rs b/vnt/tun/src/unix/fd.rs new file mode 100644 index 0000000..c377870 --- /dev/null +++ b/vnt/tun/src/unix/fd.rs @@ -0,0 +1,62 @@ +use std::io; +use std::os::fd::{AsRawFd, IntoRawFd, RawFd}; + +pub struct Fd(pub RawFd); + +impl Fd { + pub fn new(value: RawFd) -> io::Result { + if value < 0 { + return Err(io::Error::from(io::ErrorKind::InvalidInput)); + } + Ok(Fd(value)) + } +} + +impl Fd { + pub fn read(&self, buf: &mut [u8]) -> io::Result { + unsafe { + let amount = libc::read(self.0, buf.as_mut_ptr() as *mut _, buf.len()); + + if amount < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(amount as usize) + } + } + pub fn write(&self, buf: &[u8]) -> io::Result { + unsafe { + let amount = libc::write(self.0, buf.as_ptr() as *const _, buf.len()); + + if amount < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(amount as usize) + } + } +} + +impl AsRawFd for Fd { + fn as_raw_fd(&self) -> RawFd { + self.0 + } +} + +impl IntoRawFd for Fd { + fn into_raw_fd(mut self) -> RawFd { + let fd = self.0; + self.0 = -1; + fd + } +} + +impl Drop for Fd { + fn drop(&mut self) { + unsafe { + if self.0 >= 0 { + libc::close(self.0); + } + } + } +} diff --git a/vnt/tun/src/unix/mod.rs b/vnt/tun/src/unix/mod.rs new file mode 100644 index 0000000..8d4537a --- /dev/null +++ b/vnt/tun/src/unix/mod.rs @@ -0,0 +1,27 @@ +mod fd; + +pub use fd::Fd; +use std::process::Output; +#[cfg(any(target_os = "macos", target_os = "linux"))] +mod sockaddr; +#[cfg(any(target_os = "macos", target_os = "linux"))] +pub use sockaddr::SockAddr; + +#[cfg(any(target_os = "macos", target_os = "linux"))] +pub fn exe_cmd(cmd: &str) -> std::io::Result { + use std::io; + use std::process::Command; + println!("exe cmd: {}", cmd); + let out = Command::new("sh") + .arg("-c") + .arg(cmd) + .output() + .expect("sh exec error!"); + if !out.status.success() { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("cmd={},out={:?}", cmd, out), + )); + } + Ok(out) +} diff --git a/vnt/rust-tun/src/platform/posix/sockaddr.rs b/vnt/tun/src/unix/sockaddr.rs similarity index 57% rename from vnt/rust-tun/src/platform/posix/sockaddr.rs rename to vnt/tun/src/unix/sockaddr.rs index 0eaaaa0..df67f25 100644 --- a/vnt/rust-tun/src/platform/posix/sockaddr.rs +++ b/vnt/tun/src/unix/sockaddr.rs @@ -1,46 +1,17 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -use std::mem; -use std::net::Ipv4Addr; -use std::ptr; - -#[cfg(any(target_os = "macos", target_os = "ios"))] -use libc::c_uchar; -#[cfg(any(target_os = "linux", target_os = "android"))] -use libc::c_ushort; - -use libc::AF_INET as _AF_INET; use libc::{in_addr, sockaddr, sockaddr_in}; +use std::{io, mem, net::Ipv4Addr, ptr}; -use crate::error::*; +use io::Result; /// A wrapper for `sockaddr_in`. #[derive(Copy, Clone)] pub struct SockAddr(sockaddr_in); -#[cfg(any(target_os = "linux", target_os = "android"))] -const AF_INET: c_ushort = _AF_INET as c_ushort; - -#[cfg(any(target_os = "macos", target_os = "ios"))] -const AF_INET: c_uchar = _AF_INET as c_uchar; - impl SockAddr { /// Create a new `SockAddr` from a generic `sockaddr`. pub fn new(value: &sockaddr) -> Result { - if value.sa_family != AF_INET { - return Err(Error::InvalidAddress); + if value.sa_family != libc::AF_INET as libc::sa_family_t { + return Err(io::Error::new(io::ErrorKind::Other, "invalid address")); } unsafe { Self::unchecked(value) } @@ -64,7 +35,7 @@ impl From for SockAddr { let octets = ip.octets(); let mut addr = unsafe { mem::zeroed::() }; - addr.sin_family = AF_INET; + addr.sin_family = libc::AF_INET as libc::sa_family_t; addr.sin_port = 0; addr.sin_addr = in_addr { s_addr: u32::from_ne_bytes(octets), diff --git a/vnt/tun/src/windows/device.rs b/vnt/tun/src/windows/device.rs new file mode 100644 index 0000000..208e833 --- /dev/null +++ b/vnt/tun/src/windows/device.rs @@ -0,0 +1,92 @@ +use crate::device::IFace; +use crate::windows::{tap, tun}; +use std::io; +use std::net::Ipv4Addr; +use std::ops::Deref; + +pub enum Device { + Tap(tap::Device), + Tun(tun::Device), +} + +impl Device { + pub fn new(name: &str, tap: bool) -> io::Result { + if tap { + Ok(Device::Tap(tap::Device::new(name)?)) + } else { + Ok(Device::Tun(tun::Device::new(name)?)) + } + } +} + +impl IFace for Device { + fn version(&self) -> io::Result { + match self { + Device::Tap(dev) => dev.version(), + Device::Tun(dev) => dev.version(), + } + } + + fn name(&self) -> io::Result { + match self { + Device::Tap(dev) => dev.name(), + Device::Tun(dev) => dev.name(), + } + } + + fn shutdown(&self) -> io::Result<()> { + match self { + Device::Tap(dev) => dev.shutdown(), + Device::Tun(dev) => dev.shutdown(), + } + } + + fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()> { + match self { + Device::Tap(dev) => dev.set_ip(address, mask), + Device::Tun(dev) => dev.set_ip(address, mask), + } + } + + fn mtu(&self) -> io::Result { + match self { + Device::Tap(dev) => dev.mtu(), + Device::Tun(dev) => dev.mtu(), + } + } + + fn set_mtu(&self, value: u32) -> io::Result<()> { + match self { + Device::Tap(dev) => dev.set_mtu(value), + Device::Tun(dev) => dev.set_mtu(value), + } + } + + fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, metric: u16) -> io::Result<()> { + match self { + Device::Tap(dev) => dev.add_route(dest, netmask, metric), + Device::Tun(dev) => dev.add_route(dest, netmask, metric), + } + } + + fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> { + match self { + Device::Tap(dev) => dev.delete_route(dest, netmask), + Device::Tun(dev) => dev.delete_route(dest, netmask), + } + } + + fn read(&self, buf: &mut [u8]) -> io::Result { + match self { + Device::Tap(dev) => dev.read(buf), + Device::Tun(dev) => dev.read(buf), + } + } + + fn write(&self, buf: &[u8]) -> io::Result { + match self { + Device::Tap(dev) => dev.write(buf), + Device::Tun(dev) => dev.write(buf), + } + } +} diff --git a/vnt/win-tun-tap/src/ffi.rs b/vnt/tun/src/windows/ffi.rs similarity index 98% rename from vnt/win-tun-tap/src/ffi.rs rename to vnt/tun/src/windows/ffi.rs index 1b9557c..466f6cc 100644 --- a/vnt/win-tun-tap/src/ffi.rs +++ b/vnt/tun/src/windows/ffi.rs @@ -137,7 +137,7 @@ pub fn read_file(handle: HANDLE, buffer: &mut [u8]) -> io::Result { &mut ip_overlapped, ) { let e = io::Error::last_os_error(); - if e.raw_os_error().unwrap_or(0) == 997 { + if e.raw_os_error().unwrap_or(0) == ERROR_IO_PENDING as _ { if 0 == GetOverlappedResult(handle, &mut ip_overlapped, &mut ret, 1) { return Err(e); } @@ -166,7 +166,7 @@ pub fn write_file(handle: HANDLE, buffer: &[u8]) -> io::Result { &mut ip_overlapped, ) { let e = io::Error::last_os_error(); - if e.raw_os_error().unwrap_or(0) == 997 { + if e.raw_os_error().unwrap_or(0) == ERROR_IO_PENDING as _ { if 0 == GetOverlappedResult(handle, &mut ip_overlapped, &mut ret, 1) { return Err(e); } diff --git a/vnt/tun/src/windows/mod.rs b/vnt/tun/src/windows/mod.rs new file mode 100644 index 0000000..238b073 --- /dev/null +++ b/vnt/tun/src/windows/mod.rs @@ -0,0 +1,43 @@ +use std::io; +use std::os::windows::process::CommandExt; +use winapi::shared::minwindef::DWORD; +use winapi::um::winbase::CREATE_NO_WINDOW; + +mod device; +mod ffi; +mod netsh; +mod route; +mod tap; +mod tun; +pub use device::Device; + +/// Encode a string as a utf16 buffer +pub fn encode_utf16(string: &str) -> Vec { + use std::iter::once; + string.encode_utf16().chain(once(0)).collect() +} + +pub fn decode_utf16(string: &[u16]) -> String { + let end = string.iter().position(|b| *b == 0).unwrap_or(string.len()); + String::from_utf16_lossy(&string[..end]) +} + +pub const fn ctl_code(device_type: DWORD, function: DWORD, method: DWORD, access: DWORD) -> DWORD { + (device_type << 16) | (access << 14) | (function << 2) | method +} + +pub fn exe_cmd(cmd: &str) -> io::Result<()> { + println!("exe cmd: {}", cmd); + let out = std::process::Command::new("cmd") + .creation_flags(CREATE_NO_WINDOW) + .arg("/C") + .arg(&cmd) + .output()?; + if !out.status.success() { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("cmd={},out={:?}", cmd, String::from_utf8(out.stderr)), + )); + } + Ok(()) +} diff --git a/vnt/tun/src/windows/netsh.rs b/vnt/tun/src/windows/netsh.rs new file mode 100644 index 0000000..2fed74a --- /dev/null +++ b/vnt/tun/src/windows/netsh.rs @@ -0,0 +1,47 @@ +use crate::windows::exe_cmd; +use std::net::Ipv4Addr; +use std::{io, process}; + +/// 设置网卡名称 +pub fn set_interface_name(old_name: &str, new_name: &str) -> io::Result<()> { + let cmd = format!( + " netsh interface set interface name={:?} newname={:?}", + old_name, new_name + ); + exe_cmd(&cmd) +} +/// 删除缓存 +pub fn delete_cache() -> io::Result<()> { + //清除缓存 + let cmd = "netsh interface ip delete destinationcache"; + exe_cmd(cmd) +} + +/// 设置网卡ip +pub fn set_interface_ip(index: u32, address: &Ipv4Addr, netmask: &Ipv4Addr) -> io::Result<()> { + let cmd = format!( + "netsh interface ip set address {} static {:?} {:?} ", + index, address, netmask, + ); + exe_cmd(&cmd) +} + +pub fn set_interface_mtu(index: u32, mtu: u32) -> io::Result<()> { + let cmd = format!( + "netsh interface ipv4 set subinterface {} mtu={} store=persistent", + index, mtu + ); + exe_cmd(&cmd) +} +pub fn set_interface_metric(index: u32, metric: u16) -> io::Result<()> { + let cmd = format!( + "netsh interface ip set interface {} metric={}", + index, metric + ); + exe_cmd(&cmd) +} +/// 禁用ipv6 +pub fn disabled_ipv6(index: u32) -> io::Result<()> { + let cmd = format!("netsh interface ipv6 set interface {} disabled", index); + exe_cmd(&cmd) +} diff --git a/vnt/tun/src/windows/route.rs b/vnt/tun/src/windows/route.rs new file mode 100644 index 0000000..11ebfed --- /dev/null +++ b/vnt/tun/src/windows/route.rs @@ -0,0 +1,33 @@ +use std::io; +use std::net::Ipv4Addr; + +use crate::windows::exe_cmd; + +/// 添加路由 +pub fn add_route( + index: u32, + dest: Ipv4Addr, + netmask: Ipv4Addr, + gateway: Ipv4Addr, + metric: u16, +) -> io::Result<()> { + let cmd = format!( + "route add {:?} mask {:?} {:?} metric {} if {}", + dest, netmask, gateway, metric, index + ); + exe_cmd(&cmd) +} + +/// 删除路由 +pub fn delete_route( + index: u32, + dest: Ipv4Addr, + netmask: Ipv4Addr, + gateway: Ipv4Addr, +) -> io::Result<()> { + let cmd = format!( + "route delete {:?} mask {:?} {:?} if {}", + dest, netmask, gateway, index + ); + exe_cmd(&cmd) +} diff --git a/vnt/tun/src/windows/tap/mod.rs b/vnt/tun/src/windows/tap/mod.rs new file mode 100644 index 0000000..c157522 --- /dev/null +++ b/vnt/tun/src/windows/tap/mod.rs @@ -0,0 +1,193 @@ +use std::io; +use std::net::Ipv4Addr; +use winapi::shared::ifdef::NET_LUID; +use winapi::shared::minwindef::DWORD; +use winapi::um::fileapi::OPEN_EXISTING; +use winapi::um::winbase::FILE_FLAG_OVERLAPPED; +use winapi::um::winioctl::{FILE_ANY_ACCESS, FILE_DEVICE_UNKNOWN, METHOD_BUFFERED}; +use winapi::um::winnt::{ + FILE_ATTRIBUTE_SYSTEM, FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE, +}; + +use crate::device::IFace; +use crate::packet; +use crate::packet::ethernet::protocol::Protocol; +use crate::packet::{arp, ethernet}; +use crate::windows::{ctl_code, decode_utf16, encode_utf16, ffi, netsh, route}; + +/* Present in 8.1 */ +const TAP_WIN_IOCTL_GET_MAC: DWORD = + ctl_code(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS); +const TAP_WIN_IOCTL_GET_VERSION: DWORD = + ctl_code(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS); +const TAP_WIN_IOCTL_GET_MTU: DWORD = + ctl_code(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS); +const TAP_WIN_IOCTL_GET_INFO: DWORD = + ctl_code(FILE_DEVICE_UNKNOWN, 4, METHOD_BUFFERED, FILE_ANY_ACCESS); +const TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT: DWORD = + ctl_code(FILE_DEVICE_UNKNOWN, 5, METHOD_BUFFERED, FILE_ANY_ACCESS); +const TAP_WIN_IOCTL_SET_MEDIA_STATUS: DWORD = + ctl_code(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS); +const TAP_WIN_IOCTL_CONFIG_DHCP_MASQ: DWORD = + ctl_code(FILE_DEVICE_UNKNOWN, 7, METHOD_BUFFERED, FILE_ANY_ACCESS); +const TAP_WIN_IOCTL_GET_LOG_LINE: DWORD = + ctl_code(FILE_DEVICE_UNKNOWN, 8, METHOD_BUFFERED, FILE_ANY_ACCESS); +const TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT: DWORD = + ctl_code(FILE_DEVICE_UNKNOWN, 9, METHOD_BUFFERED, FILE_ANY_ACCESS); +/* Added in 8.2 */ +/* obsoletes TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT */ +const TAP_WIN_IOCTL_CONFIG_TUN: DWORD = + ctl_code(FILE_DEVICE_UNKNOWN, 10, METHOD_BUFFERED, FILE_ANY_ACCESS); + +pub struct Device { + handle: HANDLE, + index: u32, + luid: NET_LUID, + mac: [u8; 6], +} + +unsafe impl Send for Device {} + +unsafe impl Sync for Device {} + +impl Device { + /// 打开设备,设置为TUN模式,激活网卡 + pub fn new(name_str: &str) -> io::Result { + let name = encode_utf16(name_str); + let luid = ffi::alias_to_luid(&name).map_err(|e| { + io::Error::new( + e.kind(), + format!("alias_to_luid name={},err={:?}", name_str, e), + ) + })?; + let guid = ffi::luid_to_guid(&luid) + .and_then(|guid| ffi::string_from_guid(&guid)) + .map_err(|e| { + io::Error::new( + e.kind(), + format!("luid_to_guid name={},err={:?}", name_str, e), + ) + })?; + let path = format!(r"\\.\Global\{}.tap", decode_utf16(&guid)); + let handle = ffi::create_file( + &encode_utf16(&path), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + ) + .map_err(|e| io::Error::new(e.kind(), format!("tap name={},err={:?}", name_str, e)))?; + + // ep保存tun网卡的IP地址和掩码 + // let mut ep = [0;3]; + // ep[0] = Ipv4Addr::new(10,26,0,11).into(); + // ep[2] = Ipv4Addr::new(255,255,255,0).into();; + // ep[1] = ep[0] & ep[2]; + // //tun模式收不到ipv4包,原因未知 https://github.com/OpenVPN/tap-windows6/issues/111 + // ffi::device_io_control(handle, TAP_WIN_IOCTL_CONFIG_TUN, &ep, &mut ()).map_err( + // |e| { + // io::Error::new( + // e.kind(), + // format!("TAP_WIN_IOCTL_CONFIG_TUN name={},err={:?}", name_str, e), + // ) + // }, + // )?; + let mut mac = [0u8; 6]; + ffi::device_io_control(handle, TAP_WIN_IOCTL_GET_MAC, &(), &mut mac) + .map_err(|e| { + io::Error::new( + e.kind(), + format!("TAP_WIN_IOCTL_CONFIG_TUN name={},err={:?}", name_str, e), + ) + }) + .map_err(|e| io::Error::new(e.kind(), format!("TAP_WIN_IOCTL_GET_MAC,err={:?}", e)))?; + let index = ffi::luid_to_index(&luid).map(|index| index as u32)?; + // 设置网卡跃点 + netsh::set_interface_metric(index, 0)?; + let device = Self { + handle, + index, + luid, + mac, + }; + device.enabled(true)?; + Ok(device) + } + fn write_tap(&self, buf: &[u8]) -> io::Result { + ffi::write_file(self.handle, buf).map(|res| res as _) + } + fn enabled(&self, value: bool) -> io::Result<()> { + let status: u32 = if value { 1 } else { 0 }; + ffi::device_io_control( + self.handle, + TAP_WIN_IOCTL_SET_MEDIA_STATUS, + &status, + &mut (), + ) + } +} + +const MAC: [u8; 6] = [0xf, 0xf, 0xf, 0xf, 0xe, 0x9]; + +impl IFace for Device { + fn version(&self) -> io::Result { + let mut version = [0u32; 3]; + ffi::device_io_control(self.handle, TAP_WIN_IOCTL_GET_VERSION, &(), &mut version)?; + Ok(format!("{}.{}.{}", version[0], version[1], version[2])) + } + fn name(&self) -> io::Result { + ffi::luid_to_alias(&self.luid).map(|name| decode_utf16(&name)) + } + + fn shutdown(&self) -> io::Result<()> { + self.enabled(false) + } + + fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()> { + netsh::set_interface_ip(self.index, &address, &mask) + } + + fn mtu(&self) -> io::Result { + let mut mtu = 0; + ffi::device_io_control(self.handle, TAP_WIN_IOCTL_GET_MTU, &(), &mut mtu).map(|_| mtu) + } + + fn set_mtu(&self, value: u32) -> io::Result<()> { + netsh::set_interface_mtu(self.index, value) + } + + fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, metric: u16) -> io::Result<()> { + route::add_route(self.index, dest, netmask, Ipv4Addr::UNSPECIFIED, metric)?; + netsh::delete_cache() + } + + fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> { + route::delete_route(self.index, dest, netmask, Ipv4Addr::UNSPECIFIED)?; + netsh::delete_cache() + } + + fn read(&self, buf: &mut [u8]) -> io::Result { + packet::read_tap( + buf, + |eth_buf| ffi::read_file(self.handle, eth_buf).map(|res| res as usize), + |eth_buf| ffi::write_file(self.handle, eth_buf).map(|res| res as _), + ) + } + + fn write(&self, buf: &[u8]) -> io::Result { + // 封装二层数据 + packet::write_tap( + buf, + |eth_buf| ffi::write_file(self.handle, eth_buf).map(|res| res as _), + &self.mac, + ) + } +} + +impl Drop for Device { + fn drop(&mut self) { + if let Err(e) = ffi::close_handle(self.handle) { + log::warn!("close_handle={:?}", e) + } + } +} diff --git a/vnt/win-tun-tap/src/tun/mod.rs b/vnt/tun/src/windows/tun/mod.rs similarity index 52% rename from vnt/win-tun-tap/src/tun/mod.rs rename to vnt/tun/src/windows/tun/mod.rs index 95d1689..3a01dc5 100644 --- a/vnt/win-tun-tap/src/tun/mod.rs +++ b/vnt/tun/src/windows/tun/mod.rs @@ -1,11 +1,16 @@ +use libloading::{Error, Library}; use std::io; use std::net::Ipv4Addr; -use winapi::um::{synchapi, winbase, winnt}; - -use crate::{decode_utf16, encode_utf16, ffi, netsh, route, IFace}; use rand::Rng; -pub mod packet; +use winapi::um::winbase; +use winapi::um::{synchapi, winnt}; + +use crate::device::IFace; +use crate::windows::decode_utf16; +use crate::windows::{encode_utf16, ffi, netsh, route}; + +mod packet; mod wintun_log; mod wintun_raw; @@ -18,7 +23,7 @@ pub const MIN_RING_CAPACITY: u32 = 0x2_0000; /// Maximum pool name length including zero terminator pub const MAX_POOL: usize = 256; -pub struct TunDevice { +pub struct Device { pub(crate) luid: u64, pub(crate) index: u32, /// The session handle given to us by WintunStartSession @@ -39,109 +44,95 @@ pub struct TunDevice { pub(crate) adapter: wintun_raw::WINTUN_ADAPTER_HANDLE, } -unsafe impl Send for TunDevice {} +unsafe impl Send for Device {} -unsafe impl Sync for TunDevice {} +unsafe impl Sync for Device {} -impl TunDevice { - pub unsafe fn create(library: L, pool: &str, name: &str) -> io::Result - where - L: Into, - { - let win_tun = match wintun_raw::wintun::from_library(library) { - Ok(win_tun) => win_tun, - Err(e) => { +impl Device { + pub fn new(name: &str) -> io::Result { + unsafe { + let library = match Library::new("wintun.dll") { + Ok(library) => library, + Err(e) => { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("wintun.dll not found {:?}", e), + )); + } + }; + let win_tun = match wintun_raw::wintun::from_library(library) { + Ok(win_tun) => win_tun, + Err(e) => { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("library error {:?} ", e), + )); + } + }; + let name_utf16 = encode_utf16(name); + if name_utf16.len() > MAX_POOL { return Err(io::Error::new( io::ErrorKind::Other, - format!("library error {:?} ", e), + format!("too long {}:{:?}", MAX_POOL, name), )); } - }; - let pool_utf16 = encode_utf16(pool); - if pool_utf16.len() > MAX_POOL { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("长度大于{}:{:?}", MAX_POOL, pool), - )); - } - let name_utf16 = encode_utf16(name); - if name_utf16.len() > MAX_POOL { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("长度大于{}:{:?}", MAX_POOL, pool), - )); - } - let mut guid_bytes: [u8; 16] = [0u8; 16]; - rand::thread_rng().fill(&mut guid_bytes); - let guid = u128::from_ne_bytes(guid_bytes); - //SAFETY: guid is a unique integer so transmuting either all zeroes or the user's preferred - //guid to the winapi guid type is safe and will allow the windows kernel to see our GUID + wintun_log::set_default_logger_if_unset(&win_tun); + let _ = Self::delete_for_name(&win_tun, &name_utf16); + let mut guid_bytes: [u8; 16] = [0u8; 16]; + rand::thread_rng().fill(&mut guid_bytes); + let guid = u128::from_ne_bytes(guid_bytes); + //SAFETY: guid is a unique integer so transmuting either all zeroes or the user's preferred + //guid to the winapi guid type is safe and will allow the windows kernel to see our GUID - let guid_struct: wintun_raw::GUID = unsafe { std::mem::transmute(guid) }; - let guid_ptr = &guid_struct as *const wintun_raw::GUID; + let guid_struct: wintun_raw::GUID = unsafe { std::mem::transmute(guid) }; + let guid_ptr = &guid_struct as *const wintun_raw::GUID; - wintun_log::set_default_logger_if_unset(&win_tun); - - //SAFETY: the function is loaded from the wintun dll properly, we are providing valid - //pointers, and all the strings are correct null terminated UTF-16. This safety rationale - //applies for all Wintun* functions below - let adapter = - win_tun.WintunCreateAdapter(pool_utf16.as_ptr(), name_utf16.as_ptr(), guid_ptr); - if adapter.is_null() { - log::error!("adapter.is_null {:?}", io::Error::last_os_error()); - return Err(io::Error::new( - io::ErrorKind::Other, - "Failed to crate adapter", - )); - } - Self::init(win_tun, adapter) - } - pub unsafe fn init( - win_tun: wintun_raw::wintun, - adapter: wintun_raw::WINTUN_ADAPTER_HANDLE, - ) -> io::Result { - // 开启session - let session = win_tun.WintunStartSession(adapter, 128 * 1024); - if session.is_null() { - log::error!("session.is_null {:?}", io::Error::last_os_error()); - return Err(io::Error::new( - io::ErrorKind::Other, - "WintunStartSession failed", - )); - } - //SAFETY: We follow the contract required by CreateEventA. See MSDN - //(the pointers are allowed to be null, and 0 is okay for the others) - let shutdown_event = - synchapi::CreateEventA(std::ptr::null_mut(), 0, 0, std::ptr::null_mut()); - let read_event = win_tun.WintunGetReadWaitEvent(session) as winnt::HANDLE; - let mut luid: wintun_raw::NET_LUID = std::mem::zeroed(); - win_tun.WintunGetAdapterLUID(adapter, &mut luid as *mut wintun_raw::NET_LUID); - let index = ffi::luid_to_index(&std::mem::transmute(luid)).map(|index| index as u32)?; - Ok(TunDevice { - luid: std::mem::transmute(luid), - index, - session, - win_tun, - read_event, - shutdown_event, - adapter, - }) - } - pub unsafe fn delete_for_name(library: L, name: &str) -> io::Result<()> - where - L: Into, - { - let win_tun = match wintun_raw::wintun::from_library(library) { - Ok(win_tun) => win_tun, - Err(e) => { + //SAFETY: the function is loaded from the wintun dll properly, we are providing valid + //pointers, and all the strings are correct null terminated UTF-16. This safety rationale + //applies for all Wintun* functions below + let adapter = + win_tun.WintunCreateAdapter(name_utf16.as_ptr(), name_utf16.as_ptr(), guid_ptr); + if adapter.is_null() { + log::error!("adapter.is_null {:?}", io::Error::last_os_error()); return Err(io::Error::new( io::ErrorKind::Other, - format!("library error {:?} ", e), + "Failed to crate adapter", )); } - }; - wintun_log::set_default_logger_if_unset(&win_tun); - let name_utf16 = encode_utf16(name); + // 开启session + let session = win_tun.WintunStartSession(adapter, 128 * 1024); + if session.is_null() { + log::error!("session.is_null {:?}", io::Error::last_os_error()); + return Err(io::Error::new( + io::ErrorKind::Other, + "WintunStartSession failed", + )); + } + //SAFETY: We follow the contract required by CreateEventA. See MSDN + //(the pointers are allowed to be null, and 0 is okay for the others) + let shutdown_event = + synchapi::CreateEventA(std::ptr::null_mut(), 0, 0, std::ptr::null_mut()); + let read_event = win_tun.WintunGetReadWaitEvent(session) as winnt::HANDLE; + let mut luid: wintun_raw::NET_LUID = std::mem::zeroed(); + win_tun.WintunGetAdapterLUID(adapter, &mut luid as *mut wintun_raw::NET_LUID); + let index = ffi::luid_to_index(&std::mem::transmute(luid)).map(|index| index as u32)?; + // 设置网卡跃点 + netsh::set_interface_metric(index, 0)?; + Ok(Self { + luid: std::mem::transmute(luid), + index, + session, + win_tun, + read_event, + shutdown_event, + adapter, + }) + } + } + pub unsafe fn delete_for_name( + win_tun: &wintun_raw::wintun, + name_utf16: &Vec, + ) -> io::Result<()> { let adapter = win_tun.WintunOpenAdapter(name_utf16.as_ptr()); if adapter.is_null() { log::error!( @@ -157,11 +148,10 @@ impl TunDevice { win_tun.WintunDeleteDriver(); Ok(()) } - pub fn delete(self) -> io::Result<()> { - drop(self); - Ok(()) - } - pub fn version(&self) -> io::Result { +} + +impl IFace for Device { + fn version(&self) -> io::Result { let version = unsafe { self.win_tun.WintunGetRunningDriverVersion() }; if version == 0 { return Err(io::Error::new( @@ -169,78 +159,66 @@ impl TunDevice { "WintunGetRunningDriverVersion", )); } else { - Ok(Version { - major: ((version >> 16) & 0xFF) as u16, - minor: (version & 0xFF) as u16, - }) + Ok(format!("{}.{}", (version >> 16) & 0xFFFF, version & 0xFFFF)) } } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct Version { - pub major: u16, - pub minor: u16, -} - -// impl TunDevice { -// fn get_adapter_luid(&self) -> u64 { -// let mut luid: wintun_raw::NET_LUID = unsafe { std::mem::zeroed() }; -// unsafe { self.win_tun.WintunGetAdapterLUID(self.adapter, &mut luid as *mut wintun_raw::NET_LUID) }; -// unsafe { std::mem::transmute(luid) } -// } -// } - -impl IFace for TunDevice { - fn shutdown(&self) -> io::Result<()> { - // let _ = unsafe { synchapi::SetEvent(self.shutdown_event) }; - // let _ = unsafe { handleapi::CloseHandle(self.shutdown_event) }; - Ok(()) - } - - fn get_index(&self) -> io::Result { - Ok(self.index) - } - - fn get_name(&self) -> io::Result { + fn name(&self) -> io::Result { let luid = self.luid; ffi::luid_to_alias(&unsafe { std::mem::transmute(luid) }).map(|name| decode_utf16(&name)) } - fn set_name(&self, new_name: &str) -> io::Result<()> { - let name = self.get_name()?; - netsh::set_interface_name(&name, new_name) + fn shutdown(&self) -> io::Result<()> { + unsafe { + if 0 == synchapi::SetEvent(self.shutdown_event) { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } } fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()> { - netsh::set_interface_ip(self.get_index()?, &address, &mask) + netsh::set_interface_ip(self.index, &address, &mask) } - fn add_route( - &self, - dest: Ipv4Addr, - netmask: Ipv4Addr, - gateway: Ipv4Addr, - metric: u16, - ) -> io::Result<()> { - route::add_route(self.get_index()?, dest, netmask, gateway, metric) + fn mtu(&self) -> io::Result { + Err(io::Error::from(io::ErrorKind::Unsupported)) } - fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> { - route::delete_route(self.get_index()?, dest, netmask, gateway) + fn set_mtu(&self, value: u32) -> io::Result<()> { + netsh::set_interface_mtu(self.index, value) } - fn set_mtu(&self, mtu: u16) -> io::Result<()> { - netsh::set_interface_mtu(self.get_index()?, mtu) + fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, metric: u16) -> io::Result<()> { + route::add_route(self.index, dest, netmask, Ipv4Addr::UNSPECIFIED, metric)?; + netsh::delete_cache() } - fn set_metric(&self, metric: u16) -> io::Result<()> { - let index = self.get_index()?; - netsh::set_interface_metric(index, metric) + fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> { + route::delete_route(self.index, dest, netmask, Ipv4Addr::UNSPECIFIED)?; + netsh::delete_cache() + } + + fn read(&self, buf: &mut [u8]) -> io::Result { + let packet = self.receive_blocking()?; + let packet = packet.bytes(); + let len = packet.len(); + if len > buf.len() { + return Err(io::Error::new(io::ErrorKind::InvalidData, "data too long")); + } + buf[..len].copy_from_slice(packet); + Ok(len) + } + + fn write(&self, buf: &[u8]) -> io::Result { + let mut packet = self.allocate_send_packet(buf.len() as u16)?; + packet.bytes_mut().copy_from_slice(buf); + self.send_packet(packet); + Ok(buf.len()) } } -impl TunDevice { +impl Device { pub fn try_receive(&self) -> io::Result> { let mut size = 0u32; @@ -297,7 +275,7 @@ impl TunDevice { }; match result { winbase::WAIT_FAILED => { - return Err(io::Error::new(io::ErrorKind::Other, "WAIT_FAILED")) + return Err(io::Error::new(io::ErrorKind::Other, "WAIT_FAILED")); } _ => { if result == winbase::WAIT_OBJECT_0 { @@ -314,9 +292,6 @@ impl TunDevice { } } } -} - -impl TunDevice { pub fn allocate_send_packet(&self, size: u16) -> io::Result { let bytes_ptr = unsafe { self.win_tun @@ -350,13 +325,17 @@ impl TunDevice { } } -impl Drop for TunDevice { +impl Drop for Device { fn drop(&mut self) { - //Close adapter on drop - //This is why we need an Arc of wintun unsafe { + if let Err(e) = ffi::close_handle(self.shutdown_event) { + log::warn!("close shutdown_event={:?}", e) + } + self.win_tun.WintunEndSession(self.session); self.win_tun.WintunCloseAdapter(self.adapter); - self.win_tun.WintunDeleteDriver() - }; + if 0 != self.win_tun.WintunDeleteDriver() { + log::warn!("WintunDeleteDriver failed") + } + } } } diff --git a/vnt/win-tun-tap/src/tun/packet.rs b/vnt/tun/src/windows/tun/packet.rs similarity index 96% rename from vnt/win-tun-tap/src/tun/packet.rs rename to vnt/tun/src/windows/tun/packet.rs index b926588..f1b003b 100644 --- a/vnt/win-tun-tap/src/tun/packet.rs +++ b/vnt/tun/src/windows/tun/packet.rs @@ -1,4 +1,4 @@ -use crate::TunDevice; +use crate::windows::tun::Device; pub(crate) enum Kind { SendPacketPending, @@ -16,7 +16,7 @@ pub struct TunPacket<'a> { //Share ownership of session to prevent the session from being dropped before packets that //belong to it - pub(crate) tun_device: Option<&'a TunDevice>, + pub(crate) tun_device: Option<&'a Device>, } impl<'a> TunPacket<'a> { diff --git a/vnt/win-tun-tap/src/tun/wintun_log.rs b/vnt/tun/src/windows/tun/wintun_log.rs similarity index 97% rename from vnt/win-tun-tap/src/tun/wintun_log.rs rename to vnt/tun/src/windows/tun/wintun_log.rs index f694589..aa50546 100644 --- a/vnt/win-tun-tap/src/tun/wintun_log.rs +++ b/vnt/tun/src/windows/tun/wintun_log.rs @@ -1,6 +1,6 @@ use log::*; -use crate::tun::wintun_raw; +use crate::windows::tun::wintun_raw; use std::sync::atomic::{AtomicBool, Ordering}; use widestring::U16CStr; diff --git a/vnt/win-tun-tap/src/tun/wintun_raw.rs b/vnt/tun/src/windows/tun/wintun_raw.rs similarity index 100% rename from vnt/win-tun-tap/src/tun/wintun_raw.rs rename to vnt/tun/src/windows/tun/wintun_raw.rs