todo rewrite client
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
use tun::Device;
|
||||
|
||||
pub fn create_linux_tun() -> Device {
|
||||
let mut config = tun::Configuration::default();
|
||||
config
|
||||
.tun_name("netr0")
|
||||
.address((10, 0, 0, 1)) // IP нашего "моста"
|
||||
.netmask((255, 255, 255, 0)) // Маска подсети
|
||||
.up(); // Сразу включаем интерфейс
|
||||
|
||||
// Это создаст в системе интерфейс tun0
|
||||
let dev = tun::create(&config).expect("Нужны права root или CAP_NET_ADMIN!");
|
||||
dev
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use smoltcp::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken};
|
||||
use std::io::{Read, Write};
|
||||
use std::os::fd::RawFd;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
pub struct TunDevice<T: Read + Write + AsRawFd> {
|
||||
pub io: T, // Сюда мы запихнем или File (Android) или Tun-либу
|
||||
mtu: usize,
|
||||
read_buffer: BytesMut,
|
||||
}
|
||||
|
||||
impl<T: Read + Write + AsRawFd> TunDevice<T> {
|
||||
pub fn new(io: T, mtu: usize) -> Self {
|
||||
let fd = io.as_raw_fd();
|
||||
unsafe {
|
||||
let flags = libc::fcntl(fd, libc::F_GETFL);
|
||||
if flags == -1 {
|
||||
panic!("Не удалось получить флаги файла");
|
||||
}
|
||||
libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK);
|
||||
}
|
||||
Self {
|
||||
io,
|
||||
mtu,
|
||||
read_buffer: BytesMut::with_capacity(mtu),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Read + Write + AsRawFd> Device for TunDevice<T> {
|
||||
type RxToken<'a>
|
||||
= TunRxToken
|
||||
where
|
||||
Self: 'a;
|
||||
type TxToken<'a>
|
||||
= TunTxToken<'a, T>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn capabilities(&self) -> DeviceCapabilities {
|
||||
let mut caps = DeviceCapabilities::default();
|
||||
caps.medium = Medium::Ip;
|
||||
caps.max_transmission_unit = self.mtu;
|
||||
caps.checksum = ChecksumCapabilities::ignored();
|
||||
caps
|
||||
}
|
||||
|
||||
fn receive(
|
||||
&mut self,
|
||||
_timestamp: smoltcp::time::Instant,
|
||||
) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||
let mut tmp_buf = [0u8; 2048];
|
||||
match self.io.read(&mut tmp_buf) {
|
||||
Ok(n) if n > 0 => {
|
||||
let rx_data = Bytes::copy_from_slice(&tmp_buf[..n]);
|
||||
|
||||
let rx = TunRxToken { buffer: rx_data };
|
||||
let tx = TunTxToken { io: &mut self.io };
|
||||
Some((rx, tx))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
|
||||
Some(TunTxToken { io: &mut self.io })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TunRxToken {
|
||||
buffer: Bytes,
|
||||
}
|
||||
|
||||
impl RxToken for TunRxToken {
|
||||
fn consume<R, F>(mut self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&[u8]) -> R,
|
||||
{
|
||||
f(&mut self.buffer)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TunTxToken<'a, T: Write> {
|
||||
io: &'a mut T,
|
||||
}
|
||||
|
||||
impl<'a, T: Write> TxToken for TunTxToken<'a, T> {
|
||||
fn consume<R, F>(self, len: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
// ПРЯМАЯ ЗАПИСЬ БЕЗ ДОБАВЛЕНИЯ PI-ЗАГОЛОВКА
|
||||
let mut buffer = vec![0u8; len];
|
||||
let result = f(&mut buffer);
|
||||
let _ = self.io.write_all(&buffer);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Read + Write + AsRawFd> AsRawFd for TunDevice<T> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//! IP packet encapsulation
|
||||
|
||||
use std::net::IpAddr;
|
||||
|
||||
use smoltcp::wire::{IpProtocol, IpVersion, Ipv4Packet, Ipv6Packet};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IpPacket<T: AsRef<[u8]>> {
|
||||
Ipv4(Ipv4Packet<T>),
|
||||
Ipv6(Ipv6Packet<T>),
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]> + Copy> IpPacket<T> {
|
||||
pub fn new_checked(packet: T) -> smoltcp::wire::Result<Option<Self>> {
|
||||
let buffer = packet.as_ref();
|
||||
match IpVersion::of_packet(buffer)? {
|
||||
IpVersion::Ipv4 => Ok(Some(Self::Ipv4(Ipv4Packet::new_checked(packet)?))),
|
||||
IpVersion::Ipv6 => Ok(Some(Self::Ipv6(Ipv6Packet::new_checked(packet)?))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn src_addr(&self) -> IpAddr {
|
||||
match *self {
|
||||
Self::Ipv4(ref packet) => IpAddr::from(packet.src_addr()),
|
||||
Self::Ipv6(ref packet) => IpAddr::from(packet.src_addr()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dst_addr(&self) -> IpAddr {
|
||||
match *self {
|
||||
Self::Ipv4(ref packet) => IpAddr::from(packet.dst_addr()),
|
||||
Self::Ipv6(ref packet) => IpAddr::from(packet.dst_addr()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn protocol(&self) -> IpProtocol {
|
||||
match *self {
|
||||
Self::Ipv4(ref packet) => packet.next_header(),
|
||||
Self::Ipv6(ref packet) => packet.next_header(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: AsRef<[u8]> + ?Sized> IpPacket<&'a T> {
|
||||
/// Return a pointer to the payload.
|
||||
#[inline]
|
||||
pub fn payload(&self) -> &'a [u8] {
|
||||
match *self {
|
||||
IpPacket::Ipv4(ref packet) => packet.payload(),
|
||||
IpPacket::Ipv6(ref packet) => packet.payload(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
use tun::AsyncDevice;
|
||||
|
||||
//tun device
|
||||
pub fn create_linux_tun() -> AsyncDevice {
|
||||
let mut config = tun::Configuration::default();
|
||||
config
|
||||
.tun_name("netr0")
|
||||
.address((10, 0, 0, 1))
|
||||
.netmask((255, 255, 255, 0))
|
||||
.up();
|
||||
|
||||
let dev = tun::create_as_async(&config).expect("Нужны права root или CAP_NET_ADMIN!");
|
||||
dev
|
||||
}
|
||||
+204
-2
@@ -1,2 +1,204 @@
|
||||
pub mod desktop;
|
||||
pub mod device;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::{io, mem, net::IpAddr, time::Duration};
|
||||
|
||||
use byte_string::ByteStr;
|
||||
use ipnet::IpNet;
|
||||
use log::{error, info, trace, warn};
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
use tun::{
|
||||
create_as_async, AbstractDevice, AsyncDevice, Configuration as TunConfiguration,
|
||||
Error as TunError, Layer,
|
||||
};
|
||||
|
||||
use crate::tun::ip_packet::IpPacket;
|
||||
use crate::tun::virt_device::TokenBuffer;
|
||||
|
||||
mod ip_packet;
|
||||
pub mod linux_tun_device;
|
||||
pub mod virt_device;
|
||||
|
||||
/// Tun service builder
|
||||
pub struct TunBuilder {
|
||||
tun_config: TunConfiguration,
|
||||
udp_expiry_duration: Option<Duration>,
|
||||
udp_capacity: Option<usize>,
|
||||
}
|
||||
|
||||
/// TunConfiguration contains a HANDLE, which is a *mut c_void on Windows.
|
||||
unsafe impl Send for TunBuilder {}
|
||||
|
||||
impl TunBuilder {
|
||||
/// Create a Tun service builder
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tun_config: TunConfiguration::default(),
|
||||
udp_expiry_duration: None,
|
||||
udp_capacity: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn address(&mut self, addr: IpNet) {
|
||||
self.tun_config.address(addr.addr()).netmask(addr.netmask());
|
||||
}
|
||||
|
||||
pub fn destination(&mut self, addr: IpNet) {
|
||||
self.tun_config.destination(addr.addr());
|
||||
}
|
||||
|
||||
pub fn name(&mut self, name: &str) {
|
||||
self.tun_config.tun_name(name);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn file_descriptor(&mut self, fd: RawFd) {
|
||||
self.tun_config.raw_fd(fd);
|
||||
}
|
||||
|
||||
pub fn udp_expiry_duration(&mut self, udp_expiry_duration: Duration) {
|
||||
self.udp_expiry_duration = Some(udp_expiry_duration);
|
||||
}
|
||||
|
||||
pub fn udp_capacity(&mut self, udp_capacity: usize) {
|
||||
self.udp_capacity = Some(udp_capacity);
|
||||
}
|
||||
|
||||
/// Build Tun server
|
||||
pub async fn build(mut self) -> io::Result<Tun> {
|
||||
self.tun_config.layer(Layer::L3).up();
|
||||
|
||||
let device = match create_as_async(&self.tun_config) {
|
||||
Ok(d) => d,
|
||||
Err(TunError::Io(err)) => return Err(err),
|
||||
Err(err) => return Err(io::Error::other(err)),
|
||||
};
|
||||
|
||||
Ok(Tun { device })
|
||||
}
|
||||
}
|
||||
|
||||
/// Tun service
|
||||
pub struct Tun {
|
||||
device: AsyncDevice,
|
||||
}
|
||||
|
||||
impl Tun {
|
||||
/// Start serving
|
||||
pub async fn run(mut self) -> io::Result<()> {
|
||||
info!(
|
||||
"tun device {}",
|
||||
self.device
|
||||
.tun_name()
|
||||
.or_else(|r| Ok::<_, ()>(r.to_string()))
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let address = match self.device.address() {
|
||||
Ok(a) => a,
|
||||
Err(err) => {
|
||||
error!("[TUN] failed to get device address, error: {}", err);
|
||||
return Err(io::Error::other(err));
|
||||
}
|
||||
};
|
||||
|
||||
let netmask = match self.device.netmask() {
|
||||
Ok(n) => n,
|
||||
Err(err) => {
|
||||
error!("[TUN] failed to get device netmask, error: {}", err);
|
||||
return Err(io::Error::other(err));
|
||||
}
|
||||
};
|
||||
|
||||
let address_net = match IpNet::with_netmask(address, netmask) {
|
||||
Ok(n) => n,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"[TUN] invalid address {}, netmask {}, error: {}",
|
||||
address, netmask, err
|
||||
);
|
||||
return Err(io::Error::other(err));
|
||||
}
|
||||
};
|
||||
|
||||
trace!(
|
||||
"[TUN] tun device network: {} (address: {}, netmask: {})",
|
||||
address_net,
|
||||
address,
|
||||
netmask
|
||||
);
|
||||
|
||||
let address_broadcast = address_net.broadcast();
|
||||
|
||||
let create_packet_buffer = || {
|
||||
const PACKET_BUFFER_SIZE: usize = 65536;
|
||||
let mut packet_buffer = TokenBuffer::with_capacity(PACKET_BUFFER_SIZE);
|
||||
unsafe {
|
||||
packet_buffer.set_len(PACKET_BUFFER_SIZE);
|
||||
}
|
||||
packet_buffer
|
||||
};
|
||||
|
||||
let mut packet_buffer = create_packet_buffer();
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
// tun device
|
||||
n = self.device.read(&mut packet_buffer) => {
|
||||
let n = n?;
|
||||
|
||||
let mut packet_buffer = mem::replace(&mut packet_buffer, create_packet_buffer());
|
||||
unsafe {
|
||||
packet_buffer.set_len(n);
|
||||
}
|
||||
|
||||
trace!("[TUN] received IP packet {:?}", ByteStr::new(&packet_buffer));
|
||||
|
||||
if let Err(err) = self.handle_tun_frame(&address_broadcast, packet_buffer).await {
|
||||
error!("[TUN] handle IP frame failed, error: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_tun_frame(
|
||||
&mut self,
|
||||
device_broadcast_addr: &IpAddr,
|
||||
frame: TokenBuffer,
|
||||
) -> smoltcp::wire::Result<()> {
|
||||
let packet = match IpPacket::new_checked(frame.as_ref())? {
|
||||
Some(packet) => packet,
|
||||
None => {
|
||||
warn!("unrecognized IP packet {:?}", ByteStr::new(&frame));
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
trace!("[TUN] {:?}", packet);
|
||||
|
||||
let src_ip_addr = packet.src_addr();
|
||||
let dst_ip_addr = packet.dst_addr();
|
||||
let src_non_unicast = src_ip_addr == *device_broadcast_addr
|
||||
|| match src_ip_addr {
|
||||
IpAddr::V4(v4) => v4.is_broadcast() || v4.is_multicast() || v4.is_unspecified(),
|
||||
IpAddr::V6(v6) => v6.is_multicast() || v6.is_unspecified(),
|
||||
};
|
||||
let dst_non_unicast = dst_ip_addr == *device_broadcast_addr
|
||||
|| match dst_ip_addr {
|
||||
IpAddr::V4(v4) => v4.is_broadcast() || v4.is_multicast() || v4.is_unspecified(),
|
||||
IpAddr::V6(v6) => v6.is_multicast() || v6.is_unspecified(),
|
||||
};
|
||||
|
||||
if src_non_unicast || dst_non_unicast {
|
||||
trace!(
|
||||
"[TUN] IP packet {} (unicast? {}) -> {} (unicast? {}) throwing away",
|
||||
src_ip_addr,
|
||||
!src_non_unicast,
|
||||
dst_ip_addr,
|
||||
!dst_non_unicast
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
//! Virtual Device for receiving packets from tun
|
||||
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, LazyLock, Mutex,
|
||||
},
|
||||
};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use smoltcp::{
|
||||
phy::{self, Device, DeviceCapabilities},
|
||||
time::Instant,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
pub struct VirtTunDevice {
|
||||
capabilities: DeviceCapabilities,
|
||||
in_buf: mpsc::UnboundedReceiver<TokenBuffer>,
|
||||
out_buf: mpsc::UnboundedSender<TokenBuffer>,
|
||||
in_buf_avail: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl VirtTunDevice {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn new(
|
||||
capabilities: DeviceCapabilities,
|
||||
) -> (
|
||||
Self,
|
||||
mpsc::UnboundedReceiver<TokenBuffer>,
|
||||
mpsc::UnboundedSender<TokenBuffer>,
|
||||
Arc<AtomicBool>,
|
||||
) {
|
||||
let (iface_tx, iface_output) = mpsc::unbounded_channel();
|
||||
let (iface_input, iface_rx) = mpsc::unbounded_channel();
|
||||
let in_buf_avail = Arc::new(AtomicBool::new(false));
|
||||
|
||||
(
|
||||
Self {
|
||||
capabilities,
|
||||
in_buf: iface_rx,
|
||||
out_buf: iface_tx,
|
||||
in_buf_avail: in_buf_avail.clone(),
|
||||
},
|
||||
iface_output,
|
||||
iface_input,
|
||||
in_buf_avail,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn recv_available(&self) -> bool {
|
||||
self.in_buf_avail.load(Ordering::Acquire)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for VirtTunDevice {
|
||||
type RxToken<'a> = VirtRxToken<'a>;
|
||||
type TxToken<'a> = VirtTxToken<'a>;
|
||||
|
||||
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||
if let Ok(buffer) = self.in_buf.try_recv() {
|
||||
let rx = Self::RxToken {
|
||||
buffer,
|
||||
phantom_device: PhantomData,
|
||||
};
|
||||
let tx = VirtTxToken(self);
|
||||
return Some((rx, tx));
|
||||
}
|
||||
self.in_buf_avail.store(false, Ordering::Release);
|
||||
None
|
||||
}
|
||||
|
||||
fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
|
||||
Some(VirtTxToken(self))
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> DeviceCapabilities {
|
||||
self.capabilities.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VirtRxToken<'a> {
|
||||
buffer: TokenBuffer,
|
||||
phantom_device: PhantomData<&'a VirtTunDevice>,
|
||||
}
|
||||
|
||||
impl phy::RxToken for VirtRxToken<'_> {
|
||||
fn consume<R, F>(self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&[u8]) -> R,
|
||||
{
|
||||
f(&self.buffer)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VirtTxToken<'a>(&'a mut VirtTunDevice);
|
||||
|
||||
impl phy::TxToken for VirtTxToken<'_> {
|
||||
fn consume<R, F>(self, len: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
let mut buffer = TokenBuffer::with_capacity(len);
|
||||
unsafe {
|
||||
buffer.set_len(len);
|
||||
}
|
||||
|
||||
let result = f(&mut buffer);
|
||||
self.0
|
||||
.out_buf
|
||||
.send(buffer)
|
||||
.expect("channel closed unexpectedly");
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
// Maximum number of TokenBuffer cached globally.
|
||||
//
|
||||
// Each of them has capacity 65536 (defined in tun/mod.rs), so 64 * 65536 = 4MB.
|
||||
const TOKEN_BUFFER_LIST_MAX_SIZE: usize = 64;
|
||||
static TOKEN_BUFFER_LIST: LazyLock<Mutex<Vec<BytesMut>>> = LazyLock::new(|| Mutex::new(Vec::new()));
|
||||
|
||||
pub struct TokenBuffer {
|
||||
buffer: BytesMut,
|
||||
}
|
||||
|
||||
impl Drop for TokenBuffer {
|
||||
fn drop(&mut self) {
|
||||
let mut list = TOKEN_BUFFER_LIST.lock().unwrap();
|
||||
if list.len() >= TOKEN_BUFFER_LIST_MAX_SIZE {
|
||||
return;
|
||||
}
|
||||
|
||||
let empty_buffer = BytesMut::new();
|
||||
let mut buffer = mem::replace(&mut self.buffer, empty_buffer);
|
||||
buffer.clear();
|
||||
|
||||
list.push(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenBuffer {
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
let mut list = TOKEN_BUFFER_LIST.lock().unwrap();
|
||||
if let Some(mut buffer) = list.pop() {
|
||||
buffer.reserve(cap);
|
||||
return Self { buffer };
|
||||
}
|
||||
Self {
|
||||
buffer: BytesMut::with_capacity(cap),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TokenBuffer {
|
||||
type Target = BytesMut;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for TokenBuffer {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.buffer
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user