From a08fd4002bfae92072f64f8d5e0084e6f248f139 Mon Sep 17 00:00:00 2001 From: Mathias Hall-Andersen Date: Sun, 13 Oct 2019 22:26:12 +0200 Subject: [PATCH] Work on Linux platform code --- Cargo.lock | 1 + Cargo.toml | 3 + src/main.rs | 13 +- src/platform/linux/mod.rs | 179 +++++++++++++++++++ src/platform/mod.rs | 36 ++++ src/{ => wireguard}/config.rs | 12 +- src/{ => wireguard}/constants.rs | 0 src/{ => wireguard}/handshake/device.rs | 0 src/{ => wireguard}/handshake/macs.rs | 0 src/{ => wireguard}/handshake/messages.rs | 2 +- src/{ => wireguard}/handshake/mod.rs | 0 src/{ => wireguard}/handshake/noise.rs | 2 +- src/{ => wireguard}/handshake/peer.rs | 0 src/{ => wireguard}/handshake/ratelimiter.rs | 0 src/{ => wireguard}/handshake/timestamp.rs | 0 src/{ => wireguard}/handshake/types.rs | 2 +- src/wireguard/mod.rs | 23 +++ src/{ => wireguard}/router/anti_replay.rs | 0 src/{ => wireguard}/router/constants.rs | 0 src/{ => wireguard}/router/device.rs | 24 +-- src/{ => wireguard}/router/ip.rs | 0 src/{ => wireguard}/router/messages.rs | 0 src/{ => wireguard}/router/mod.rs | 0 src/{ => wireguard}/router/peer.rs | 2 +- src/{ => wireguard}/router/tests.rs | 3 +- src/{ => wireguard}/router/types.rs | 2 - src/{ => wireguard}/router/workers.rs | 0 src/{ => wireguard}/tests.rs | 6 +- src/{ => wireguard}/timers.rs | 8 +- src/{ => wireguard}/types/bind.rs | 0 src/{ => wireguard}/types/dummy.rs | 0 src/{ => wireguard}/types/endpoint.rs | 0 src/{ => wireguard}/types/keys.rs | 0 src/{ => wireguard}/types/mod.rs | 0 src/{ => wireguard}/types/tun.rs | 0 src/{ => wireguard}/wireguard.rs | 27 +-- 36 files changed, 293 insertions(+), 52 deletions(-) create mode 100644 src/platform/linux/mod.rs create mode 100644 src/platform/mod.rs rename src/{ => wireguard}/config.rs (96%) rename src/{ => wireguard}/constants.rs (100%) rename src/{ => wireguard}/handshake/device.rs (100%) rename src/{ => wireguard}/handshake/macs.rs (100%) rename src/{ => wireguard}/handshake/messages.rs (99%) rename src/{ => wireguard}/handshake/mod.rs (100%) rename src/{ => wireguard}/handshake/noise.rs (99%) rename src/{ => wireguard}/handshake/peer.rs (100%) rename src/{ => wireguard}/handshake/ratelimiter.rs (100%) rename src/{ => wireguard}/handshake/timestamp.rs (100%) rename src/{ => wireguard}/handshake/types.rs (98%) create mode 100644 src/wireguard/mod.rs rename src/{ => wireguard}/router/anti_replay.rs (100%) rename src/{ => wireguard}/router/constants.rs (100%) rename src/{ => wireguard}/router/device.rs (90%) rename src/{ => wireguard}/router/ip.rs (100%) rename src/{ => wireguard}/router/messages.rs (100%) rename src/{ => wireguard}/router/mod.rs (100%) rename src/{ => wireguard}/router/peer.rs (99%) rename src/{ => wireguard}/router/tests.rs (99%) rename src/{ => wireguard}/router/types.rs (98%) rename src/{ => wireguard}/router/workers.rs (100%) rename src/{ => wireguard}/tests.rs (92%) rename src/{ => wireguard}/timers.rs (98%) rename src/{ => wireguard}/types/bind.rs (100%) rename src/{ => wireguard}/types/dummy.rs (100%) rename src/{ => wireguard}/types/endpoint.rs (100%) rename src/{ => wireguard}/types/keys.rs (100%) rename src/{ => wireguard}/types/mod.rs (100%) rename src/{ => wireguard}/types/tun.rs (100%) rename src/{ => wireguard}/wireguard.rs (96%) diff --git a/Cargo.lock b/Cargo.lock index a833b4c..e0fab55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1601,6 +1601,7 @@ dependencies = [ "hjul 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 5049bc6..768514b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,9 @@ env_logger = "0.6" num_cpus = "^1.10" jemallocator = "0.3.0" +[target.'cfg(unix)'.dependencies] +libc = "0.2" + [dependencies.x25519-dalek] version = "^0.5" diff --git a/src/main.rs b/src/main.rs index 3c59c67..aad8d02 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,14 +7,11 @@ extern crate jemallocator; static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; // mod config; -mod constants; -mod handshake; -mod router; -mod timers; -mod types; +mod platform; mod wireguard; -#[cfg(test)] -mod tests; +use platform::TunBind; -fn main() {} +fn main() { + let (readers, writers, mtu) = platform::PlatformTun::create("test").unwrap(); +} diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs new file mode 100644 index 0000000..ad2b8be --- /dev/null +++ b/src/platform/linux/mod.rs @@ -0,0 +1,179 @@ +use super::Tun; +use super::TunBind; + +use super::super::wireguard::tun::*; + +use libc::*; + +use std::os::raw::c_short; +use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + +const IFNAMSIZ: usize = 16; +const TUNSETIFF: u64 = 0x4004_54ca; + +const IFF_UP: i16 = 0x1; +const IFF_RUNNING: i16 = 0x40; + +const IFF_TUN: c_short = 0x0001; +const IFF_NO_PI: c_short = 0x1000; + +use std::error::Error; +use std::fmt; +use std::sync::atomic::AtomicUsize; +use std::sync::Arc; + +const CLONE_DEVICE_PATH: &'static [u8] = b"/dev/net/tun\0"; + +const TUN_MAGIC: u8 = b'T'; +const TUN_SET_IFF: u8 = 202; + +#[repr(C)] +struct Ifreq { + name: [u8; libc::IFNAMSIZ], + flags: c_short, + _pad: [u8; 64], +} + +pub struct PlatformTun {} + +pub struct PlatformTunReader { + fd: RawFd, +} + +pub struct PlatformTunWriter { + fd: RawFd, +} + +/* Listens for netlink messages + * announcing an MTU update for the interface + */ +#[derive(Clone)] +pub struct PlatformTunMTU { + value: Arc, +} + +#[derive(Debug)] +pub enum LinuxTunError { + InvalidTunDeviceName, + FailedToOpenCloneDevice, + SetIFFIoctlFailed, + Closed, // TODO +} + +impl fmt::Display for LinuxTunError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + unimplemented!() + } +} + +impl Error for LinuxTunError { + fn description(&self) -> &str { + unimplemented!() + } + + fn source(&self) -> Option<&(dyn Error + 'static)> { + unimplemented!() + } +} + +impl MTU for PlatformTunMTU { + fn mtu(&self) -> usize { + 1500 + } +} + +impl Reader for PlatformTunReader { + type Error = LinuxTunError; + + fn read(&self, buf: &mut [u8], offset: usize) -> Result { + debug_assert!( + offset < buf.len(), + "There is no space for the body of the TUN read" + ); + let n = unsafe { read(self.fd, buf[offset..].as_mut_ptr() as _, buf.len() - offset) }; + if n < 0 { + Err(LinuxTunError::Closed) + } else { + // conversion is safe + Ok(n as usize) + } + } +} + +impl Writer for PlatformTunWriter { + type Error = LinuxTunError; + + fn write(&self, src: &[u8]) -> Result<(), Self::Error> { + match unsafe { write(self.fd, src.as_ptr() as _, src.len() as _) } { + -1 => Err(LinuxTunError::Closed), + _ => Ok(()), + } + } +} + +impl Tun for PlatformTun { + type Error = LinuxTunError; + type Reader = PlatformTunReader; + type Writer = PlatformTunWriter; + type MTU = PlatformTunMTU; +} + +impl TunBind for PlatformTun { + fn create(name: &str) -> Result<(Vec, Self::Writer, Self::MTU), Self::Error> { + // construct request struct + let mut req = Ifreq { + name: [0u8; libc::IFNAMSIZ], + flags: (libc::IFF_TUN | libc::IFF_NO_PI) as c_short, + _pad: [0u8; 64], + }; + + // sanity check length of device name + let bs = name.as_bytes(); + if bs.len() > libc::IFNAMSIZ - 1 { + return Err(LinuxTunError::InvalidTunDeviceName); + } + req.name[..bs.len()].copy_from_slice(bs); + + // open clone device + let fd = match unsafe { open(CLONE_DEVICE_PATH.as_ptr() as _, O_RDWR) } { + -1 => return Err(LinuxTunError::FailedToOpenCloneDevice), + fd => fd, + }; + + // create TUN device + if unsafe { ioctl(fd, TUNSETIFF as _, &req) } < 0 { + return Err(LinuxTunError::SetIFFIoctlFailed); + } + + // create PlatformTunMTU instance + + Ok(( + vec![PlatformTunReader { fd }], + PlatformTunWriter { fd }, + PlatformTunMTU { + value: Arc::new(AtomicUsize::new(1500)), + }, + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::env; + + fn is_root() -> bool { + match env::var("USER") { + Ok(val) => val == "root", + Err(e) => false, + } + } + + #[test] + fn test_tun_create() { + if !is_root() { + return; + } + let (readers, writers, mtu) = PlatformTun::create("test").unwrap(); + } +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs new file mode 100644 index 0000000..e83384c --- /dev/null +++ b/src/platform/mod.rs @@ -0,0 +1,36 @@ +use std::error::Error; + +use super::wireguard::bind::Bind; +use super::wireguard::tun::Tun; + +#[cfg(target_os = "linux")] +mod linux; + +#[cfg(target_os = "linux")] +pub use linux::PlatformTun; + +/* Syntax is nasty here, due to open issue: + * https://github.com/rust-lang/rust/issues/38078 + */ +pub trait UDPBind { + type Closer; + type Error: Error; + type Bind: Bind; + + /// Bind to a new port, returning the reader/writer and + /// an associated instance of the Closer type, which closes the UDP socket upon "drop". + fn bind( + port: u16, + ) -> Result< + ( + <::Bind as Bind>::Reader, + <::Bind as Bind>::Writer, + Self::Closer, + ), + Self::Error, + >; +} + +pub trait TunBind: Tun { + fn create(name: &str) -> Result<(Vec, Self::Writer, Self::MTU), Self::Error>; +} diff --git a/src/config.rs b/src/wireguard/config.rs similarity index 96% rename from src/config.rs rename to src/wireguard/config.rs index 3376dcc..0f2953d 100644 --- a/src/config.rs +++ b/src/wireguard/config.rs @@ -1,12 +1,10 @@ -use std::error::Error; use std::net::{IpAddr, SocketAddr}; use x25519_dalek::{PublicKey, StaticSecret}; -use crate::wireguard::Wireguard; -use crate::types::tun::Tun; -use crate::types::bind::Bind; +use super::wireguard::Wireguard; +use super::types::bind::Bind; +use super::types::tun::Tun; -/// /// The goal of the configuration interface is, among others, /// to hide the IO implementations (over which the WG device is generic), /// from the configuration and UAPI code. @@ -180,5 +178,9 @@ impl Configuration for Wireguard { fn set_listen_port(&self, port : u16) -> Option { None } + + fn set_fwmark(&self, mark: Option) -> Option { + None + } } \ No newline at end of file diff --git a/src/constants.rs b/src/wireguard/constants.rs similarity index 100% rename from src/constants.rs rename to src/wireguard/constants.rs diff --git a/src/handshake/device.rs b/src/wireguard/handshake/device.rs similarity index 100% rename from src/handshake/device.rs rename to src/wireguard/handshake/device.rs diff --git a/src/handshake/macs.rs b/src/wireguard/handshake/macs.rs similarity index 100% rename from src/handshake/macs.rs rename to src/wireguard/handshake/macs.rs diff --git a/src/handshake/messages.rs b/src/wireguard/handshake/messages.rs similarity index 99% rename from src/handshake/messages.rs rename to src/wireguard/handshake/messages.rs index 796e3c0..29d80af 100644 --- a/src/handshake/messages.rs +++ b/src/wireguard/handshake/messages.rs @@ -4,7 +4,6 @@ use hex; #[cfg(test)] use std::fmt; -use std::cmp; use std::mem; use byteorder::LittleEndian; @@ -265,6 +264,7 @@ impl fmt::Debug for MacsFooter { /* Equality (for testing purposes) */ +#[cfg(test)] macro_rules! eq_as_bytes { ($type:path) => { impl PartialEq for $type { diff --git a/src/handshake/mod.rs b/src/wireguard/handshake/mod.rs similarity index 100% rename from src/handshake/mod.rs rename to src/wireguard/handshake/mod.rs diff --git a/src/handshake/noise.rs b/src/wireguard/handshake/noise.rs similarity index 99% rename from src/handshake/noise.rs rename to src/wireguard/handshake/noise.rs index 1dc8402..a2a84b0 100644 --- a/src/handshake/noise.rs +++ b/src/wireguard/handshake/noise.rs @@ -27,7 +27,7 @@ use super::peer::{Peer, State}; use super::timestamp; use super::types::*; -use crate::types::{Key, KeyPair}; +use super::super::types::{KeyPair, Key}; use std::time::Instant; diff --git a/src/handshake/peer.rs b/src/wireguard/handshake/peer.rs similarity index 100% rename from src/handshake/peer.rs rename to src/wireguard/handshake/peer.rs diff --git a/src/handshake/ratelimiter.rs b/src/wireguard/handshake/ratelimiter.rs similarity index 100% rename from src/handshake/ratelimiter.rs rename to src/wireguard/handshake/ratelimiter.rs diff --git a/src/handshake/timestamp.rs b/src/wireguard/handshake/timestamp.rs similarity index 100% rename from src/handshake/timestamp.rs rename to src/wireguard/handshake/timestamp.rs diff --git a/src/handshake/types.rs b/src/wireguard/handshake/types.rs similarity index 98% rename from src/handshake/types.rs rename to src/wireguard/handshake/types.rs index ba71ec4..5f984cc 100644 --- a/src/handshake/types.rs +++ b/src/wireguard/handshake/types.rs @@ -3,7 +3,7 @@ use std::fmt; use x25519_dalek::PublicKey; -use crate::types::KeyPair; +use super::super::types::KeyPair; /* Internal types for the noise IKpsk2 implementation */ diff --git a/src/wireguard/mod.rs b/src/wireguard/mod.rs new file mode 100644 index 0000000..9417e57 --- /dev/null +++ b/src/wireguard/mod.rs @@ -0,0 +1,23 @@ +mod wireguard; +// mod config; +mod constants; +mod timers; + +mod handshake; +mod router; +mod types; + +#[cfg(test)] +mod tests; + +/// The WireGuard sub-module contains a pure, configurable implementation of WireGuard. +/// The implementation is generic over: +/// +/// - TUN type, specifying how packets are received on the interface side: a reader/writer and MTU reporting interface. +/// - Bind type, specifying how WireGuard messages are sent/received from the internet and what constitutes an "endpoint" + +pub use wireguard::{Wireguard, Peer}; + +pub use types::bind; +pub use types::tun; +pub use types::Endpoint; \ No newline at end of file diff --git a/src/router/anti_replay.rs b/src/wireguard/router/anti_replay.rs similarity index 100% rename from src/router/anti_replay.rs rename to src/wireguard/router/anti_replay.rs diff --git a/src/router/constants.rs b/src/wireguard/router/constants.rs similarity index 100% rename from src/router/constants.rs rename to src/wireguard/router/constants.rs diff --git a/src/router/device.rs b/src/wireguard/router/device.rs similarity index 90% rename from src/router/device.rs rename to src/wireguard/router/device.rs index 989c2c2..455020c 100644 --- a/src/router/device.rs +++ b/src/wireguard/router/device.rs @@ -21,9 +21,9 @@ use super::types::{Callbacks, RouterError}; use super::workers::{worker_parallel, JobParallel, Operation}; use super::SIZE_MESSAGE_PREFIX; -use super::super::types::{KeyPair, Endpoint, bind, tun}; +use super::super::types::{bind, tun, Endpoint, KeyPair}; -pub struct DeviceInner> { +pub struct DeviceInner> { // inbound writer (TUN) pub inbound: T, @@ -47,7 +47,7 @@ pub struct EncryptionState { pub death: Instant, // (birth + reject-after-time - keepalive-timeout - rekey-timeout) } -pub struct DecryptionState> { +pub struct DecryptionState> { pub keypair: Arc, pub confirmed: AtomicBool, pub protector: Mutex, @@ -55,12 +55,12 @@ pub struct DecryptionState> { - state: Arc>, // reference to device state +pub struct Device> { + state: Arc>, // reference to device state handles: Vec>, // join handles for workers } -impl> Drop for Device { +impl> Drop for Device { fn drop(&mut self) { debug!("router: dropping device"); @@ -85,7 +85,7 @@ impl> Drop for De } #[inline(always)] -fn get_route>( +fn get_route>( device: &Arc>, packet: &[u8], ) -> Option>> { @@ -124,10 +124,10 @@ fn get_route>( } } -impl> Device { +impl> Device { pub fn new(num_workers: usize, tun: T) -> Device { // allocate shared device state - let mut inner = DeviceInner { + let inner = DeviceInner { inbound: tun, outbound: RwLock::new(None), queues: Mutex::new(Vec::with_capacity(num_workers)), @@ -235,9 +235,9 @@ impl> Device> PeerInner>, - mut msg: Vec, + msg: Vec, ) -> Option { let (tx, rx) = oneshot(); let key = dec.keypair.recv.key; diff --git a/src/router/tests.rs b/src/wireguard/router/tests.rs similarity index 99% rename from src/router/tests.rs rename to src/wireguard/router/tests.rs index 6c385a8..fbee39e 100644 --- a/src/router/tests.rs +++ b/src/wireguard/router/tests.rs @@ -10,7 +10,6 @@ use pnet::packet::ipv4::MutableIpv4Packet; use pnet::packet::ipv6::MutableIpv6Packet; use super::super::types::bind::*; -use super::super::types::tun::*; use super::super::types::*; use super::{Callbacks, Device, SIZE_MESSAGE_PREFIX}; @@ -146,7 +145,7 @@ mod tests { // create device let (_fake, _reader, tun_writer, _mtu) = dummy::TunTest::create(1500, false); - let router: Device< _, BencherCallbacks, dummy::TunWriter, dummy::VoidBind> = + let router: Device<_, BencherCallbacks, dummy::TunWriter, dummy::VoidBind> = Device::new(num_cpus::get(), tun_writer); // add new peer diff --git a/src/router/types.rs b/src/wireguard/router/types.rs similarity index 98% rename from src/router/types.rs rename to src/wireguard/router/types.rs index 4a72c27..b7c3ae0 100644 --- a/src/router/types.rs +++ b/src/wireguard/router/types.rs @@ -1,8 +1,6 @@ use std::error::Error; use std::fmt; -use super::super::types::Endpoint; - pub trait Opaque: Send + Sync + 'static {} impl Opaque for T where T: Send + Sync + 'static {} diff --git a/src/router/workers.rs b/src/wireguard/router/workers.rs similarity index 100% rename from src/router/workers.rs rename to src/wireguard/router/workers.rs diff --git a/src/tests.rs b/src/wireguard/tests.rs similarity index 92% rename from src/tests.rs rename to src/wireguard/tests.rs index 8e15037..0148d5d 100644 --- a/src/tests.rs +++ b/src/wireguard/tests.rs @@ -1,6 +1,6 @@ -use crate::types::tun::Tun; -use crate::types::{bind, dummy, tun}; -use crate::wireguard::Wireguard; +use super::types::tun::Tun; +use super::types::{bind, dummy, tun}; +use super::wireguard::Wireguard; use std::thread; use std::time::Duration; diff --git a/src/timers.rs b/src/wireguard/timers.rs similarity index 98% rename from src/timers.rs rename to src/wireguard/timers.rs index 9854229..2792c7b 100644 --- a/src/timers.rs +++ b/src/wireguard/timers.rs @@ -7,10 +7,10 @@ use log::info; use hjul::{Runner, Timer}; -use crate::constants::*; -use crate::router::Callbacks; -use crate::types::{bind, tun}; -use crate::wireguard::{Peer, PeerInner}; +use super::constants::*; +use super::router::Callbacks; +use super::types::{bind, tun}; +use super::wireguard::{Peer, PeerInner}; pub struct Timers { handshake_pending: AtomicBool, diff --git a/src/types/bind.rs b/src/wireguard/types/bind.rs similarity index 100% rename from src/types/bind.rs rename to src/wireguard/types/bind.rs diff --git a/src/types/dummy.rs b/src/wireguard/types/dummy.rs similarity index 100% rename from src/types/dummy.rs rename to src/wireguard/types/dummy.rs diff --git a/src/types/endpoint.rs b/src/wireguard/types/endpoint.rs similarity index 100% rename from src/types/endpoint.rs rename to src/wireguard/types/endpoint.rs diff --git a/src/types/keys.rs b/src/wireguard/types/keys.rs similarity index 100% rename from src/types/keys.rs rename to src/wireguard/types/keys.rs diff --git a/src/types/mod.rs b/src/wireguard/types/mod.rs similarity index 100% rename from src/types/mod.rs rename to src/wireguard/types/mod.rs diff --git a/src/types/tun.rs b/src/wireguard/types/tun.rs similarity index 100% rename from src/types/tun.rs rename to src/wireguard/types/tun.rs diff --git a/src/wireguard.rs b/src/wireguard/wireguard.rs similarity index 96% rename from src/wireguard.rs rename to src/wireguard/wireguard.rs index f14a053..7a22280 100644 --- a/src/wireguard.rs +++ b/src/wireguard/wireguard.rs @@ -1,13 +1,12 @@ -use crate::constants::*; -use crate::handshake; -use crate::router; -use crate::timers::{Events, Timers}; +use super::constants::*; +use super::handshake; +use super::router; +use super::timers::{Events, Timers}; -use crate::types::bind::Reader as BindReader; -use crate::types::bind::{Bind, Writer}; -use crate::types::tun::{Reader, Tun, MTU}; - -use crate::types::Endpoint; +use super::types::bind::Reader as BindReader; +use super::types::bind::{Bind, Writer}; +use super::types::tun::{Reader, Tun, MTU}; +use super::types::Endpoint; use hjul::Runner; @@ -372,9 +371,13 @@ impl Wireguard { msg.resize(size, 0); // read a new IP packet - let payload = reader - .read(&mut msg[..], router::SIZE_MESSAGE_PREFIX) - .unwrap(); + let payload = match reader.read(&mut msg[..], router::SIZE_MESSAGE_PREFIX) { + Ok(payload) => payload, + Err(e) => { + debug!("TUN worker, failed to read from tun device: {}", e); + return; + } + }; debug!("TUN worker, IP packet of {} bytes (MTU = {})", payload, mtu); // truncate padding