Skip to content

Commit

Permalink
Implement support for windows tunnel
Browse files Browse the repository at this point in the history
Signed-off-by: Lee Smet <lee.smet@hotmail.com>
  • Loading branch information
LeeSmet committed Feb 14, 2024
1 parent a16f4e4 commit ab34195
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 4 deletions.
37 changes: 35 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ tun = { git = "https://github.com/LeeSmet/rust-tun", features = ["async"] }
libc = "0.2.150"
nix = { version = "0.27.1", features = ["net", "socket", "ioctl"] }

[target.'cfg(target_os = "windows")'.dependencies]
wintun = "0.4.0"

[profile.release]
lto = "fat"
codegen-units = 1
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,11 @@ impl Stack {
tun_rx,
)
} else {
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
{
panic!("On this platform, you can only run with --no-tun");
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
{
let (rxhalf, txhalf) = tun::new(
&config.tun_name,
Expand Down
5 changes: 5 additions & 0 deletions src/tun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ pub use linux::new;
mod darwin;
#[cfg(target_os = "macos")]
pub use darwin::new;

#[cfg(target_os = "windows")]
mod windows;
#[cfg(target_os = "windows")]
pub use windows::new;
99 changes: 99 additions & 0 deletions src/tun/windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use std::{io, ops::Deref, sync::Arc};

use futures::{Sink, Stream};
use log::{error, info};
use tokio::sync::mpsc;

use crate::{crypto::PacketBuffer, subnet::Subnet};

// TODO
const LINK_MTU: usize = 1400;

/// Type of the tunnel used, specified when creating the tunnel.
// TODO: verify if this is correct.
const WINDOWS_TUNNEL_TYPE: &str = "Wintun";

pub async fn new(
name: &str,
node_subnet: Subnet,
route_subnet: Subnet,
) -> Result<
(
impl Stream<Item = io::Result<PacketBuffer>>,
impl Sink<PacketBuffer, Error = impl std::error::Error> + Clone,
),
Box<dyn std::error::Error>,
> {
// SAFETY: for now we assume a valid wintun.dll file exists in tehe root directory when we are
// running this.
let wintun = unsafe { wintun::load() }?;
let tun = wintun::Adapter::create(&wintun, name, WINDOWS_TUNNEL_TYPE, None)?;
// Configure created network adapter.
tun.set_mtu(LINK_MTU)?;
// Set address, this will use a `netsh` command under the hood unfortunately.
tun.set_network_addresses_tuple(node_subnet.address(), route_subnet.mask(), None)?;
// Build 2 separate sessions - one for receiving, one for sending.
let rx_session = Arc::new(tun.start_session(wintun::MAX_RING_CAPACITY)?);
let tx_session = rx_session.clone();

let (tun_sink, mut sink_receiver) = mpsc::channel::<PacketBuffer>(1000);
let (tun_stream, stream_receiver) = mpsc::unbounded_channel();

// Ingress path
tokio::task::spawn_blocking(move || {
loop {
let packet = rx_session.receive_blocking().map(|tun_packet| {
let mut buffer = PacketBuffer::new();
// SAFETY: The configured MTU is smaller than the static PacketBuffer size.
let packet_len = tun_packet.bytes().len();
buffer.buffer_mut()[..packet_len].copy_from_slice(tun_packet.bytes_mut());
buffer.set_size(packet_len);
buffer
});

if tun_stream.send(packet).is_err() {
error!("Could not forward data to tun stream, receiver is gone");
break;
};
}

info!("Stop reading from tun interface");
});

// Egress path
tokio::task::spawn_blocking(move || {
loop {
match sink_receiver.blocking_recv() {
None => break,
Some(data) => {
let mut tun_packet =
match tx_session.allocate_send_packet(data.deref().len() as u16) {
Ok(tun_packet) => tun_packet,
Err(e) => {
error!("Could not allocate packet on TUN: {e}");
break;
}
};
// SAFETY: packet allocation is done on the length of &data.
tun_packet.bytes_mut().copy_from_slice(&data);
tx_session.send_packet(tun_packet);
}
}
}
info!("Stop writing to tun interface");
});

Ok((
tokio_stream::wrappers::UnboundedReceiverStream::new(stream_receiver),
tokio_util::sync::PollSender::new(tun_sink),
))
}

impl From<wintun::Error> for io::Error {
fn from(value: wintun::Error) -> Self {
match value {
wintun::Error::Io(e) => e,
_ => io::Error::new(io::ErrorKind::Other, "unknown wintun error"),
}
}
}

0 comments on commit ab34195

Please sign in to comment.