[mio] 合并各平台的tun/tap处理

This commit is contained in:
lubeilin
2024-02-29 22:20:30 +08:00
parent 2f6d743931
commit 27a6c686a7
31 changed files with 1858 additions and 227 deletions
+32
View File
@@ -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"
]}
+56
View File
@@ -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<Self> {
Ok(Self { fd: Fd::new(fd)? })
}
}
impl IFace for Device {
fn version(&self) -> io::Result<String> {
Ok(String::new())
}
fn name(&self) -> io::Result<String> {
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<u32> {
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<usize> {
self.fd.read(buf)
}
fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.fd.write(buf)
}
}
+24
View File
@@ -0,0 +1,24 @@
use io::Result;
use std::io;
use std::net::Ipv4Addr;
pub trait IFace {
fn version(&self) -> Result<String>;
/// Get the device name.
fn name(&self) -> Result<String>;
fn shutdown(&self) -> Result<()>;
fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> Result<()>;
/// Get the MTU.
fn mtu(&self) -> Result<u32>;
/// 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<usize>;
fn write(&self, buf: &[u8]) -> Result<usize>;
}
+31
View File
@@ -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;
+303
View File
@@ -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<Self> {
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<Ipv4Addr> {
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<Ipv4Addr> {
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<Ipv4Addr> {
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<Ipv4Addr> {
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<String> {
Ok(String::new())
}
fn name(&self) -> io::Result<String> {
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<u32> {
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<usize> {
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<usize> {
if let Some(mac) = &self.mac {
packet::write_tap(buf, |eth_buf| self.tun.write(eth_buf), mac)
} else {
self.tun.write(buf)
}
}
}
+4
View File
@@ -0,0 +1,4 @@
mod device;
pub use device::Device;
mod route;
mod sys;
+16
View File
@@ -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(())
}
+21
View File
@@ -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);
+286
View File
@@ -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<Self> {
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::<u32>()
.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::<sockaddr_ctl>() 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<Ipv4Addr> {
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<Ipv4Addr> {
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<Ipv4Addr> {
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<Ipv4Addr> {
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<String> {
Ok(String::new())
}
fn name(&self) -> io::Result<String> {
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<u32> {
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<usize> {
self.tun.read(buf)
}
fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.tun.write(buf)
}
}
+5
View File
@@ -0,0 +1,5 @@
mod device;
pub use device::Device;
mod sys;
mod route;
+18
View File
@@ -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)
}
@@ -1,35 +1,11 @@
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
// Version 2, December 2004
//
// Copyleft (ↄ) meh. <[email protected]> | 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 {
+1
View File
@@ -0,0 +1 @@
pub mod packet;
+122
View File
@@ -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<B> {
buffer: B,
}
impl<B: AsRef<[u8]>> ArpPacket<B> {
pub fn unchecked(buffer: B) -> Self {
Self { buffer }
}
pub fn new(buffer: B) -> io::Result<Self> {
if buffer.as_ref().len() != 28 {
Err(io::Error::from(io::ErrorKind::InvalidData))?;
}
let packet = Self::unchecked(buffer);
Ok(packet)
}
}
impl<B: AsRef<[u8]>> ArpPacket<B> {
/// 硬件类型 以太网类型为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响应,3RARP请求,4RARP响应
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<B: AsRef<[u8]> + AsMut<[u8]>> ArpPacket<B> {
/// 硬件类型 以太网类型为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响应,3RARP请求,4RARP响应
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<B: AsRef<[u8]>> fmt::Debug for ArpPacket<B> {
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()
}
}
+2
View File
@@ -0,0 +1,2 @@
pub mod packet;
pub mod protocol;
+77
View File
@@ -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<B> {
pub buffer: B,
}
impl<B: AsRef<[u8]>> EthernetPacket<B> {
pub fn unchecked(buffer: B) -> EthernetPacket<B> {
EthernetPacket { buffer }
}
pub fn new(buffer: B) -> io::Result<EthernetPacket<B>> {
let packet = EthernetPacket::unchecked(buffer);
//头部固定14位
if packet.buffer.as_ref().len() < 14 {
Err(io::Error::from(io::ErrorKind::InvalidData))?;
}
Ok(packet)
}
}
impl<B: AsRef<[u8]>> EthernetPacket<B> {
/// 目的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<B: AsRef<[u8]> + AsMut<[u8]>> EthernetPacket<B> {
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<B: AsRef<[u8]>> fmt::Debug for EthernetPacket<B> {
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()
}
}
+141
View File
@@ -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<u16> 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<u16> 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,
}
}
}
+64
View File
@@ -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<W, R>(buf: &mut [u8], read_fn: R, write_fn: W) -> io::Result<usize>
where
W: Fn(&[u8]) -> io::Result<usize>,
R: Fn(&mut [u8]) -> io::Result<usize>,
{
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<W>(buf: &[u8], write_fn: W, mac: &[u8; 6]) -> io::Result<usize>
where
W: Fn(&[u8]) -> io::Result<usize>,
{
// 封装二层数据
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(&ether.buffer)
}
+62
View File
@@ -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<Self> {
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<usize> {
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<usize> {
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);
}
}
}
}
+27
View File
@@ -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<Output> {
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)
}
@@ -1,46 +1,17 @@
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
// Version 2, December 2004
//
// Copyleft (ↄ) meh. <[email protected]> | 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<Self> {
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<Ipv4Addr> for SockAddr {
let octets = ip.octets();
let mut addr = unsafe { mem::zeroed::<sockaddr_in>() };
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),
+92
View File
@@ -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<Self> {
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<String> {
match self {
Device::Tap(dev) => dev.version(),
Device::Tun(dev) => dev.version(),
}
}
fn name(&self) -> io::Result<String> {
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<u32> {
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<usize> {
match self {
Device::Tap(dev) => dev.read(buf),
Device::Tun(dev) => dev.read(buf),
}
}
fn write(&self, buf: &[u8]) -> io::Result<usize> {
match self {
Device::Tap(dev) => dev.write(buf),
Device::Tun(dev) => dev.write(buf),
}
}
}
@@ -137,7 +137,7 @@ pub fn read_file(handle: HANDLE, buffer: &mut [u8]) -> io::Result<DWORD> {
&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<DWORD> {
&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);
}
+43
View File
@@ -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<u16> {
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(())
}
+47
View File
@@ -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)
}
+33
View File
@@ -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)
}
+193
View File
@@ -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<Self> {
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<usize> {
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<String> {
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<String> {
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<u32> {
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<usize> {
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<usize> {
// 封装二层数据
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)
}
}
}
@@ -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<L>(library: L, pool: &str, name: &str) -> io::Result<Self>
where
L: Into<libloading::Library>,
{
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<Self> {
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<Self> {
// 开启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<L>(library: L, name: &str) -> io::Result<()>
where
L: Into<libloading::Library>,
{
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<u16>,
) -> 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<Version> {
}
impl IFace for Device {
fn version(&self) -> io::Result<String> {
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<u32> {
Ok(self.index)
}
fn get_name(&self) -> io::Result<String> {
fn name(&self) -> io::Result<String> {
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<u32> {
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<usize> {
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<usize> {
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<Option<packet::TunPacket>> {
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<packet::TunPacket> {
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")
}
}
}
}
@@ -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> {
@@ -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;