Work on Linux platform code

This commit is contained in:
Mathias Hall-Andersen
2019-10-13 22:26:12 +02:00
parent 6000cbf7e4
commit a08fd4002b
36 changed files with 293 additions and 52 deletions

1
Cargo.lock generated
View File

@@ -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)",

View File

@@ -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"

View File

@@ -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();
}

179
src/platform/linux/mod.rs Normal file
View File

@@ -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<AtomicUsize>,
}
#[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<usize, Self::Error> {
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::Reader>, 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();
}
}

36
src/platform/mod.rs Normal file
View File

@@ -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<
(
<<Self as UDPBind>::Bind as Bind>::Reader,
<<Self as UDPBind>::Bind as Bind>::Writer,
Self::Closer,
),
Self::Error,
>;
}
pub trait TunBind: Tun {
fn create(name: &str) -> Result<(Vec<Self::Reader>, Self::Writer, Self::MTU), Self::Error>;
}

View File

@@ -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.
@@ -181,4 +179,8 @@ impl <T : Tun, B : Bind>Configuration for Wireguard<T, B> {
None
}
fn set_fwmark(&self, mark: Option<u32>) -> Option<ConfigError> {
None
}
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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 */

23
src/wireguard/mod.rs Normal file
View File

@@ -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;

View File

@@ -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<E : Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> {
pub struct DeviceInner<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> {
// 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<E : Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> {
pub struct DecryptionState<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> {
pub keypair: Arc<KeyPair>,
pub confirmed: AtomicBool,
pub protector: Mutex<AntiReplay>,
@@ -55,12 +55,12 @@ pub struct DecryptionState<E : Endpoint, C: Callbacks, T: tun::Writer, B: bind::
pub death: Instant, // time when the key can no longer be used for decryption
}
pub struct Device<E : Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> {
pub struct Device<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> {
state: Arc<DeviceInner<E, C, T, B>>, // reference to device state
handles: Vec<thread::JoinHandle<()>>, // join handles for workers
}
impl<E : Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> Drop for Device<E, C, T, B> {
impl<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> Drop for Device<E, C, T, B> {
fn drop(&mut self) {
debug!("router: dropping device");
@@ -85,7 +85,7 @@ impl<E : Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> Drop for De
}
#[inline(always)]
fn get_route<E : Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>>(
fn get_route<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>>(
device: &Arc<DeviceInner<E, C, T, B>>,
packet: &[u8],
) -> Option<Arc<PeerInner<E, C, T, B>>> {
@@ -124,10 +124,10 @@ fn get_route<E : Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>>(
}
}
impl<E : Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> Device<E, C, T, B> {
impl<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> Device<E, C, T, B> {
pub fn new(num_workers: usize, tun: T) -> Device<E, C, T, B> {
// 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)),
@@ -237,7 +237,7 @@ impl<E : Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> Device<E, C
/// Set outbound writer
///
///
pub fn set_outbound_writer(&self, new : B) {
pub fn set_outbound_writer(&self, new: B) {
*self.state.outbound.write() = Some(new);
}
}

View File

@@ -291,7 +291,7 @@ impl<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> PeerInner<E,
&self,
src: E,
dec: Arc<DecryptionState<E, C, T, B>>,
mut msg: Vec<u8>,
msg: Vec<u8>,
) -> Option<JobParallel> {
let (tx, rx) = oneshot();
let key = dec.keypair.recv.key;

View File

@@ -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

View File

@@ -1,8 +1,6 @@
use std::error::Error;
use std::fmt;
use super::super::types::Endpoint;
pub trait Opaque: Send + Sync + 'static {}
impl<T> Opaque for T where T: Send + Sync + 'static {}

View File

@@ -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;

View File

@@ -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,

View File

@@ -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<T: Tun, B: Bind> Wireguard<T, B> {
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