Work on Linux platform code
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -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)",
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
13
src/main.rs
13
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();
|
||||
}
|
||||
|
||||
179
src/platform/linux/mod.rs
Normal file
179
src/platform/linux/mod.rs
Normal 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
36
src/platform/mod.rs
Normal 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>;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
|
||||
@@ -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
23
src/wireguard/mod.rs
Normal 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;
|
||||
@@ -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>> {
|
||||
state: Arc<DeviceInner<E, C, T, B>>, // reference to device state
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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 {}
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user