rip linux code from smoltcp

This commit is contained in:
ssrlive 2023-09-03 12:34:01 +08:00
parent 8b014322fc
commit 073eed0b5c
4 changed files with 291 additions and 2 deletions

View file

@ -17,6 +17,10 @@ mod http;
pub mod setup;
mod socks;
mod tun2proxy;
#[cfg(any(target_os = "linux", target_os = "android"))]
mod tuntapinterface;
#[cfg(any(target_os = "linux", target_os = "android"))]
mod tuntapinterfacedesc;
mod virtdevice;
mod virtdns;

View file

@ -1,5 +1,7 @@
#![allow(dead_code)]
#[cfg(any(target_os = "linux", target_os = "android"))]
use crate::tuntapinterface::TunTapInterface;
use crate::{dns, error::Error, error::Result, virtdevice::VirtualTunDevice, NetworkInterface, Options};
#[cfg(target_family = "unix")]
use mio::unix::SourceFd;
@ -8,8 +10,8 @@ use mio::{event::Event, net::TcpStream, net::UdpSocket, Events, Interest, Poll,
use smoltcp::phy::DeviceCapabilities;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use smoltcp::phy::RawSocket;
#[cfg(any(target_os = "linux", target_os = "android"))]
use smoltcp::phy::TunTapInterface;
// #[cfg(any(target_os = "linux", target_os = "android"))]
// use smoltcp::phy::TunTapInterface;
#[cfg(target_family = "unix")]
use smoltcp::phy::{Device, Medium, RxToken, TxToken};
use smoltcp::{

129
src/tuntapinterface.rs Normal file
View file

@ -0,0 +1,129 @@
use crate::tuntapinterfacedesc::TunTapInterfaceDesc;
use smoltcp::{
phy::{self, Device, DeviceCapabilities, Medium},
time::Instant,
};
use std::{
cell::RefCell,
io,
os::unix::io::{AsRawFd, RawFd},
rc::Rc,
vec::Vec,
};
/// A virtual TUN (IP) or TAP (Ethernet) interface.
#[derive(Debug)]
pub struct TunTapInterface {
lower: Rc<RefCell<TunTapInterfaceDesc>>,
mtu: usize,
medium: Medium,
}
impl AsRawFd for TunTapInterface {
fn as_raw_fd(&self) -> RawFd {
self.lower.borrow().as_raw_fd()
}
}
impl TunTapInterface {
/// Attaches to a TUN/TAP interface called `name`, or creates it if it does not exist.
///
/// If `name` is a persistent interface configured with UID of the current user,
/// no special privileges are needed. Otherwise, this requires superuser privileges
/// or a corresponding capability set on the executable.
pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterface> {
let lower = TunTapInterfaceDesc::new(name, medium)?;
let mtu = lower.interface_mtu()?;
Ok(TunTapInterface {
lower: Rc::new(RefCell::new(lower)),
mtu,
medium,
})
}
/// Attaches to a TUN/TAP interface specified by file descriptor `fd`.
///
/// On platforms like Android, a file descriptor to a tun interface is exposed.
/// On these platforms, a TunTapInterface cannot be instantiated with a name.
pub fn from_fd(fd: RawFd, medium: Medium, mtu: usize) -> io::Result<TunTapInterface> {
let lower = TunTapInterfaceDesc::from_fd(fd, mtu)?;
Ok(TunTapInterface {
lower: Rc::new(RefCell::new(lower)),
mtu,
medium,
})
}
}
impl Device for TunTapInterface {
type RxToken<'a> = RxToken;
type TxToken<'a> = TxToken;
fn capabilities(&self) -> DeviceCapabilities {
let mut v = DeviceCapabilities::default();
v.max_transmission_unit = self.mtu;
v.medium = self.medium;
v
}
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
let mut lower = self.lower.borrow_mut();
let mut buffer = vec![0; self.mtu];
match lower.recv(&mut buffer[..]) {
Ok(size) => {
buffer.resize(size, 0);
let rx = RxToken { buffer };
let tx = TxToken {
lower: self.lower.clone(),
};
Some((rx, tx))
}
Err(err) if err.kind() == io::ErrorKind::WouldBlock => None,
Err(err) => panic!("{}", err),
}
}
fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
Some(TxToken {
lower: self.lower.clone(),
})
}
}
#[doc(hidden)]
pub struct RxToken {
buffer: Vec<u8>,
}
impl phy::RxToken for RxToken {
fn consume<R, F>(mut self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
f(&mut self.buffer[..])
}
}
#[doc(hidden)]
pub struct TxToken {
lower: Rc<RefCell<TunTapInterfaceDesc>>,
}
impl phy::TxToken for TxToken {
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
let mut lower = self.lower.borrow_mut();
let mut buffer = vec![0; len];
let result = f(&mut buffer);
match lower.send(&buffer[..]) {
Ok(_) => {}
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {
log::error!("phy: tx failed due to WouldBlock")
}
Err(err) => panic!("{}", err),
}
result
}
}

154
src/tuntapinterfacedesc.rs Normal file
View file

@ -0,0 +1,154 @@
use smoltcp::{phy::Medium, wire::EthernetFrame};
use std::{
io,
os::unix::io::{AsRawFd, RawFd},
};
#[derive(Debug)]
pub struct TunTapInterfaceDesc {
lower: libc::c_int,
mtu: usize,
}
impl AsRawFd for TunTapInterfaceDesc {
fn as_raw_fd(&self) -> RawFd {
self.lower
}
}
impl TunTapInterfaceDesc {
pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterfaceDesc> {
let lower = unsafe {
let lower = libc::open(
"/dev/net/tun\0".as_ptr() as *const libc::c_char,
libc::O_RDWR | libc::O_NONBLOCK,
);
if lower == -1 {
return Err(io::Error::last_os_error());
}
lower
};
let mut ifreq = ifreq_for(name);
Self::attach_interface_ifreq(lower, medium, &mut ifreq)?;
let mtu = Self::mtu_ifreq(medium, &mut ifreq)?;
Ok(TunTapInterfaceDesc { lower, mtu })
}
pub fn from_fd(fd: RawFd, mtu: usize) -> io::Result<TunTapInterfaceDesc> {
Ok(TunTapInterfaceDesc { lower: fd, mtu })
}
fn attach_interface_ifreq(lower: libc::c_int, medium: Medium, ifr: &mut Ifreq) -> io::Result<()> {
let mode = match medium {
Medium::Ip => imp::IFF_TUN,
Medium::Ethernet => imp::IFF_TAP,
Medium::Ieee802154 => todo!(),
};
ifr.ifr_data = mode | imp::IFF_NO_PI;
ifreq_ioctl(lower, ifr, imp::TUNSETIFF).map(|_| ())
}
fn mtu_ifreq(medium: Medium, ifr: &mut Ifreq) -> io::Result<usize> {
let lower = unsafe {
let lower = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP);
if lower == -1 {
return Err(io::Error::last_os_error());
}
lower
};
let ip_mtu = ifreq_ioctl(lower, ifr, imp::SIOCGIFMTU).map(|mtu| mtu as usize);
unsafe {
libc::close(lower);
}
// Propagate error after close, to ensure we always close.
let ip_mtu = ip_mtu?;
// SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)
// smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it.
let mtu = match medium {
Medium::Ip => ip_mtu,
Medium::Ethernet => ip_mtu + EthernetFrame::<&[u8]>::header_len(),
Medium::Ieee802154 => todo!(),
};
Ok(mtu)
}
pub fn interface_mtu(&self) -> io::Result<usize> {
Ok(self.mtu)
}
pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
unsafe {
let len = libc::read(self.lower, buffer.as_mut_ptr() as *mut libc::c_void, buffer.len());
if len == -1 {
return Err(io::Error::last_os_error());
}
Ok(len as usize)
}
}
pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
unsafe {
let len = libc::write(self.lower, buffer.as_ptr() as *const libc::c_void, buffer.len());
if len == -1 {
return Err(io::Error::last_os_error());
}
Ok(len as usize)
}
}
}
impl Drop for TunTapInterfaceDesc {
fn drop(&mut self) {
unsafe {
libc::close(self.lower);
}
}
}
#[repr(C)]
#[derive(Debug)]
struct Ifreq {
ifr_name: [libc::c_char; libc::IF_NAMESIZE],
ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */
}
fn ifreq_for(name: &str) -> Ifreq {
let mut ifreq = Ifreq {
ifr_name: [0; libc::IF_NAMESIZE],
ifr_data: 0,
};
for (i, byte) in name.as_bytes().iter().enumerate() {
ifreq.ifr_name[i] = *byte as libc::c_char
}
ifreq
}
fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut Ifreq, cmd: libc::c_ulong) -> io::Result<libc::c_int> {
unsafe {
let res = libc::ioctl(lower, cmd as _, ifreq as *mut Ifreq);
if res == -1 {
return Err(io::Error::last_os_error());
}
}
Ok(ifreq.ifr_data)
}
mod imp {
pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
// pub const SIOCGIFINDEX: libc::c_ulong = 0x8933;
// pub const ETH_P_ALL: libc::c_short = 0x0003;
// pub const ETH_P_IEEE802154: libc::c_short = 0x00F6;
pub const TUNSETIFF: libc::c_ulong = 0x400454CA;
pub const IFF_TUN: libc::c_int = 0x0001;
pub const IFF_TAP: libc::c_int = 0x0002;
pub const IFF_NO_PI: libc::c_int = 0x1000;
}