aboutsummaryrefslogtreecommitdiffstats
path: root/src/platform
diff options
context:
space:
mode:
authorMathias Hall-Andersen <mathias@hall-andersen.dk>2019-10-23 12:08:35 +0200
committerMathias Hall-Andersen <mathias@hall-andersen.dk>2019-10-23 12:08:35 +0200
commitee3599d5507ceee23ef3382dbda9de8e73c54a00 (patch)
treed681a3f8a5a2d5e7bea779acecd1fc0798285d9e /src/platform
parentWork on platform specific code (Linux) (diff)
downloadwireguard-rs-ee3599d5507ceee23ef3382dbda9de8e73c54a00.tar.xz
wireguard-rs-ee3599d5507ceee23ef3382dbda9de8e73c54a00.zip
Moved IO traits into platform module
Diffstat (limited to 'src/platform')
-rw-r--r--src/platform/bind.rs43
-rw-r--r--src/platform/dummy.rs22
-rw-r--r--src/platform/dummy/bind.rs211
-rw-r--r--src/platform/dummy/endpoint.rs1
-rw-r--r--src/platform/dummy/mod.rs13
-rw-r--r--src/platform/dummy/tun.rs172
-rw-r--r--src/platform/endpoint.rs7
-rw-r--r--src/platform/linux/tun.rs6
-rw-r--r--src/platform/linux/udp.rs3
-rw-r--r--src/platform/mod.rs32
-rw-r--r--src/platform/tun.rs61
11 files changed, 518 insertions, 53 deletions
diff --git a/src/platform/bind.rs b/src/platform/bind.rs
new file mode 100644
index 0000000..f22a5d7
--- /dev/null
+++ b/src/platform/bind.rs
@@ -0,0 +1,43 @@
+use super::Endpoint;
+use std::error::Error;
+
+pub trait Reader<E: Endpoint>: Send + Sync {
+ type Error: Error;
+
+ fn read(&self, buf: &mut [u8]) -> Result<(usize, E), Self::Error>;
+}
+
+pub trait Writer<E: Endpoint>: Send + Sync + Clone + 'static {
+ type Error: Error;
+
+ fn write(&self, buf: &[u8], dst: &E) -> Result<(), Self::Error>;
+}
+
+pub trait Bind: Send + Sync + 'static {
+ type Error: Error;
+ type Endpoint: Endpoint;
+
+ /* Until Rust gets type equality constraints these have to be generic */
+ type Writer: Writer<Self::Endpoint>;
+ type Reader: Reader<Self::Endpoint>;
+}
+
+/// On platforms where fwmark can be set and the
+/// implementation can bind to a new port during later configuration (UAPI support),
+/// this type provides the ability to set the fwmark and close the socket (by dropping the instance)
+pub trait Owner: Send {
+ type Error: Error;
+
+ fn set_fwmark(&self, value: Option<u32>) -> Option<Self::Error>;
+}
+
+/// On some platforms the application can itself bind to a socket.
+/// This enables configuration using the UAPI interface.
+pub trait Platform: Bind {
+ type Owner: Owner;
+
+ /// Bind to a new port, returning the reader/writer and
+ /// an associated instance of the owner type, which closes the UDP socket upon "drop"
+ /// and enables configuration of the fwmark value.
+ fn bind(port: u16) -> Result<(Vec<Self::Reader>, Self::Writer, Self::Owner), Self::Error>;
+}
diff --git a/src/platform/dummy.rs b/src/platform/dummy.rs
deleted file mode 100644
index 208febe..0000000
--- a/src/platform/dummy.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-#[cfg(test)]
-use super::super::wireguard::dummy;
-use super::BindOwner;
-use super::PlatformBind;
-
-pub struct VoidOwner {}
-
-impl BindOwner for VoidOwner {
- type Error = dummy::BindError;
-
- fn set_fwmark(&self, value: Option<u32>) -> Option<Self::Error> {
- None
- }
-}
-
-impl PlatformBind for dummy::PairBind {
- type Owner = VoidOwner;
-
- fn bind(_port: u16) -> Result<(Vec<Self::Reader>, Self::Writer, Self::Owner), Self::Error> {
- Err(dummy::BindError::Disconnected)
- }
-}
diff --git a/src/platform/dummy/bind.rs b/src/platform/dummy/bind.rs
new file mode 100644
index 0000000..14143ae
--- /dev/null
+++ b/src/platform/dummy/bind.rs
@@ -0,0 +1,211 @@
+use std::error::Error;
+use std::fmt;
+use std::marker;
+use std::net::SocketAddr;
+use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
+use std::sync::Arc;
+use std::sync::Mutex;
+
+use super::super::bind::*;
+use super::super::Endpoint;
+
+pub struct VoidOwner {}
+
+#[derive(Debug)]
+pub enum BindError {
+ Disconnected,
+}
+
+impl Error for BindError {
+ fn description(&self) -> &str {
+ "Generic Bind Error"
+ }
+
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ None
+ }
+}
+
+impl fmt::Display for BindError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ BindError::Disconnected => write!(f, "PairBind disconnected"),
+ }
+ }
+}
+
+/* TUN implementation */
+
+#[derive(Debug)]
+pub enum TunError {
+ Disconnected,
+}
+
+impl Error for TunError {
+ fn description(&self) -> &str {
+ "Generic Tun Error"
+ }
+
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ None
+ }
+}
+
+impl fmt::Display for TunError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Not Possible")
+ }
+}
+
+/* Endpoint implementation */
+
+#[derive(Clone, Copy)]
+pub struct UnitEndpoint {}
+
+impl Endpoint for UnitEndpoint {
+ fn from_address(_: SocketAddr) -> UnitEndpoint {
+ UnitEndpoint {}
+ }
+
+ fn into_address(&self) -> SocketAddr {
+ "127.0.0.1:8080".parse().unwrap()
+ }
+
+ fn clear_src(&mut self) {}
+}
+
+impl UnitEndpoint {
+ pub fn new() -> UnitEndpoint {
+ UnitEndpoint {}
+ }
+}
+
+#[derive(Clone, Copy)]
+pub struct VoidBind {}
+
+impl Reader<UnitEndpoint> for VoidBind {
+ type Error = BindError;
+
+ fn read(&self, _buf: &mut [u8]) -> Result<(usize, UnitEndpoint), Self::Error> {
+ Ok((0, UnitEndpoint {}))
+ }
+}
+
+impl Writer<UnitEndpoint> for VoidBind {
+ type Error = BindError;
+
+ fn write(&self, _buf: &[u8], _dst: &UnitEndpoint) -> Result<(), Self::Error> {
+ Ok(())
+ }
+}
+
+impl Bind for VoidBind {
+ type Error = BindError;
+ type Endpoint = UnitEndpoint;
+
+ type Reader = VoidBind;
+ type Writer = VoidBind;
+}
+
+impl VoidBind {
+ pub fn new() -> VoidBind {
+ VoidBind {}
+ }
+}
+
+/* Pair Bind */
+
+#[derive(Clone)]
+pub struct PairReader<E> {
+ recv: Arc<Mutex<Receiver<Vec<u8>>>>,
+ _marker: marker::PhantomData<E>,
+}
+
+impl Reader<UnitEndpoint> for PairReader<UnitEndpoint> {
+ type Error = BindError;
+ fn read(&self, buf: &mut [u8]) -> Result<(usize, UnitEndpoint), Self::Error> {
+ let vec = self
+ .recv
+ .lock()
+ .unwrap()
+ .recv()
+ .map_err(|_| BindError::Disconnected)?;
+ let len = vec.len();
+ buf[..len].copy_from_slice(&vec[..]);
+ Ok((vec.len(), UnitEndpoint {}))
+ }
+}
+
+impl Writer<UnitEndpoint> for PairWriter<UnitEndpoint> {
+ type Error = BindError;
+ fn write(&self, buf: &[u8], _dst: &UnitEndpoint) -> Result<(), Self::Error> {
+ let owned = buf.to_owned();
+ match self.send.lock().unwrap().send(owned) {
+ Err(_) => Err(BindError::Disconnected),
+ Ok(_) => Ok(()),
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct PairWriter<E> {
+ send: Arc<Mutex<SyncSender<Vec<u8>>>>,
+ _marker: marker::PhantomData<E>,
+}
+
+#[derive(Clone)]
+pub struct PairBind {}
+
+impl PairBind {
+ pub fn pair<E>() -> (
+ (PairReader<E>, PairWriter<E>),
+ (PairReader<E>, PairWriter<E>),
+ ) {
+ let (tx1, rx1) = sync_channel(128);
+ let (tx2, rx2) = sync_channel(128);
+ (
+ (
+ PairReader {
+ recv: Arc::new(Mutex::new(rx1)),
+ _marker: marker::PhantomData,
+ },
+ PairWriter {
+ send: Arc::new(Mutex::new(tx2)),
+ _marker: marker::PhantomData,
+ },
+ ),
+ (
+ PairReader {
+ recv: Arc::new(Mutex::new(rx2)),
+ _marker: marker::PhantomData,
+ },
+ PairWriter {
+ send: Arc::new(Mutex::new(tx1)),
+ _marker: marker::PhantomData,
+ },
+ ),
+ )
+ }
+}
+
+impl Bind for PairBind {
+ type Error = BindError;
+ type Endpoint = UnitEndpoint;
+ type Reader = PairReader<Self::Endpoint>;
+ type Writer = PairWriter<Self::Endpoint>;
+}
+
+impl Owner for VoidOwner {
+ type Error = BindError;
+
+ fn set_fwmark(&self, _value: Option<u32>) -> Option<Self::Error> {
+ None
+ }
+}
+
+impl Platform for PairBind {
+ type Owner = VoidOwner;
+ fn bind(_port: u16) -> Result<(Vec<Self::Reader>, Self::Writer, Self::Owner), Self::Error> {
+ Err(BindError::Disconnected)
+ }
+}
diff --git a/src/platform/dummy/endpoint.rs b/src/platform/dummy/endpoint.rs
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/platform/dummy/endpoint.rs
@@ -0,0 +1 @@
+
diff --git a/src/platform/dummy/mod.rs b/src/platform/dummy/mod.rs
new file mode 100644
index 0000000..884bd7e
--- /dev/null
+++ b/src/platform/dummy/mod.rs
@@ -0,0 +1,13 @@
+mod bind;
+mod endpoint;
+mod tun;
+
+/* A pure dummy platform available during "test-time"
+ *
+ * The use of the dummy platform is to enable unit testing of full WireGuard,
+ * the configuration interface and the UAPI parser.
+ */
+
+pub use bind::*;
+pub use endpoint::*;
+pub use tun::*;
diff --git a/src/platform/dummy/tun.rs b/src/platform/dummy/tun.rs
new file mode 100644
index 0000000..9fe9480
--- /dev/null
+++ b/src/platform/dummy/tun.rs
@@ -0,0 +1,172 @@
+use std::error::Error;
+use std::fmt;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
+use std::sync::Arc;
+use std::sync::Mutex;
+
+use super::super::tun::*;
+
+/* This submodule provides pure/dummy implementations of the IO interfaces
+ * for use in unit tests thoughout the project.
+ */
+
+/* Error implementation */
+
+#[derive(Debug)]
+pub enum BindError {
+ Disconnected,
+}
+
+impl Error for BindError {
+ fn description(&self) -> &str {
+ "Generic Bind Error"
+ }
+
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ None
+ }
+}
+
+impl fmt::Display for BindError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ BindError::Disconnected => write!(f, "PairBind disconnected"),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum TunError {
+ Disconnected,
+}
+
+impl Error for TunError {
+ fn description(&self) -> &str {
+ "Generic Tun Error"
+ }
+
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ None
+ }
+}
+
+impl fmt::Display for TunError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Not Possible")
+ }
+}
+
+pub struct TunTest {}
+
+pub struct TunFakeIO {
+ store: bool,
+ tx: SyncSender<Vec<u8>>,
+ rx: Receiver<Vec<u8>>,
+}
+
+pub struct TunReader {
+ rx: Receiver<Vec<u8>>,
+}
+
+pub struct TunWriter {
+ store: bool,
+ tx: Mutex<SyncSender<Vec<u8>>>,
+}
+
+#[derive(Clone)]
+pub struct TunMTU {
+ mtu: Arc<AtomicUsize>,
+}
+
+impl Reader for TunReader {
+ type Error = TunError;
+
+ fn read(&self, buf: &mut [u8], offset: usize) -> Result<usize, Self::Error> {
+ match self.rx.recv() {
+ Ok(m) => {
+ buf[offset..].copy_from_slice(&m[..]);
+ Ok(m.len())
+ }
+ Err(_) => Err(TunError::Disconnected),
+ }
+ }
+}
+
+impl Writer for TunWriter {
+ type Error = TunError;
+
+ fn write(&self, src: &[u8]) -> Result<(), Self::Error> {
+ if self.store {
+ let m = src.to_owned();
+ match self.tx.lock().unwrap().send(m) {
+ Ok(_) => Ok(()),
+ Err(_) => Err(TunError::Disconnected),
+ }
+ } else {
+ Ok(())
+ }
+ }
+}
+
+impl MTU for TunMTU {
+ fn mtu(&self) -> usize {
+ self.mtu.load(Ordering::Acquire)
+ }
+}
+
+impl Tun for TunTest {
+ type Writer = TunWriter;
+ type Reader = TunReader;
+ type MTU = TunMTU;
+ type Error = TunError;
+}
+
+impl TunFakeIO {
+ pub fn write(&self, msg: Vec<u8>) {
+ if self.store {
+ self.tx.send(msg).unwrap();
+ }
+ }
+
+ pub fn read(&self) -> Vec<u8> {
+ self.rx.recv().unwrap()
+ }
+}
+
+impl TunTest {
+ pub fn create(mtu: usize, store: bool) -> (TunFakeIO, TunReader, TunWriter, TunMTU) {
+ let (tx1, rx1) = if store {
+ sync_channel(32)
+ } else {
+ sync_channel(1)
+ };
+ let (tx2, rx2) = if store {
+ sync_channel(32)
+ } else {
+ sync_channel(1)
+ };
+
+ let fake = TunFakeIO {
+ tx: tx1,
+ rx: rx2,
+ store,
+ };
+ let reader = TunReader { rx: rx1 };
+ let writer = TunWriter {
+ tx: Mutex::new(tx2),
+ store,
+ };
+ let mtu = TunMTU {
+ mtu: Arc::new(AtomicUsize::new(mtu)),
+ };
+
+ (fake, reader, writer, mtu)
+ }
+}
+
+impl Platform for TunTest {
+ fn create(_name: &str) -> Result<(Vec<Self::Reader>, Self::Writer, Self::MTU), Self::Error> {
+ Err(TunError::Disconnected)
+ }
+}
diff --git a/src/platform/endpoint.rs b/src/platform/endpoint.rs
new file mode 100644
index 0000000..4702aab
--- /dev/null
+++ b/src/platform/endpoint.rs
@@ -0,0 +1,7 @@
+use std::net::SocketAddr;
+
+pub trait Endpoint: Send + 'static {
+ fn from_address(addr: SocketAddr) -> Self;
+ fn into_address(&self) -> SocketAddr;
+ fn clear_src(&mut self);
+}
diff --git a/src/platform/linux/tun.rs b/src/platform/linux/tun.rs
index 5b7b105..090569a 100644
--- a/src/platform/linux/tun.rs
+++ b/src/platform/linux/tun.rs
@@ -1,6 +1,4 @@
-use super::super::super::wireguard::tun::*;
-use super::super::PlatformTun;
-use super::super::Tun;
+use super::super::tun::*;
use libc::*;
@@ -127,7 +125,7 @@ impl Tun for LinuxTun {
type MTU = LinuxTunMTU;
}
-impl PlatformTun for LinuxTun {
+impl Platform for LinuxTun {
fn create(name: &str) -> Result<(Vec<Self::Reader>, Self::Writer, Self::MTU), Self::Error> {
// construct request struct
let mut req = Ifreq {
diff --git a/src/platform/linux/udp.rs b/src/platform/linux/udp.rs
index 0a1a186..52e4c45 100644
--- a/src/platform/linux/udp.rs
+++ b/src/platform/linux/udp.rs
@@ -1,6 +1,5 @@
-use super::super::Bind;
+use super::super::bind::*;
use super::super::Endpoint;
-use super::super::PlatformBind;
use std::net::SocketAddr;
diff --git a/src/platform/mod.rs b/src/platform/mod.rs
index a0bbc13..ecd559a 100644
--- a/src/platform/mod.rs
+++ b/src/platform/mod.rs
@@ -1,33 +1,15 @@
-use std::error::Error;
+mod endpoint;
-use super::wireguard::bind::Bind;
-use super::wireguard::tun::Tun;
-use super::wireguard::Endpoint;
+pub mod bind;
+pub mod tun;
-#[cfg(test)]
-mod dummy;
+pub use endpoint::Endpoint;
#[cfg(target_os = "linux")]
mod linux;
+#[cfg(test)]
+pub mod dummy;
+
#[cfg(target_os = "linux")]
pub use linux::LinuxTun as TunInstance;
-
-pub trait BindOwner: Send {
- type Error: Error;
-
- fn set_fwmark(&self, value: Option<u32>) -> Option<Self::Error>;
-}
-
-pub trait PlatformBind: Bind {
- type Owner: BindOwner;
-
- /// Bind to a new port, returning the reader/writer and
- /// an associated instance of the owner type, which closes the UDP socket upon "drop"
- /// and enables configuration of the fwmark value.
- fn bind(port: u16) -> Result<(Vec<Self::Reader>, Self::Writer, Self::Owner), Self::Error>;
-}
-
-pub trait PlatformTun: Tun {
- fn create(name: &str) -> Result<(Vec<Self::Reader>, Self::Writer, Self::MTU), Self::Error>;
-}
diff --git a/src/platform/tun.rs b/src/platform/tun.rs
new file mode 100644
index 0000000..f49d4af
--- /dev/null
+++ b/src/platform/tun.rs
@@ -0,0 +1,61 @@
+use std::error::Error;
+
+pub trait Writer: Send + Sync + 'static {
+ type Error: Error;
+
+ /// Receive a cryptkey routed IP packet
+ ///
+ /// # Arguments
+ ///
+ /// - src: Buffer containing the IP packet to be written
+ ///
+ /// # Returns
+ ///
+ /// Unit type or an error
+ fn write(&self, src: &[u8]) -> Result<(), Self::Error>;
+}
+
+pub trait Reader: Send + 'static {
+ type Error: Error;
+
+ /// Reads an IP packet into dst[offset:] from the tunnel device
+ ///
+ /// The reason for providing space for a prefix
+ /// is to efficiently accommodate platforms on which the packet is prefaced by a header.
+ /// This space is later used to construct the transport message inplace.
+ ///
+ /// # Arguments
+ ///
+ /// - buf: Destination buffer (enough space for MTU bytes + header)
+ /// - offset: Offset for the beginning of the IP packet
+ ///
+ /// # Returns
+ ///
+ /// The size of the IP packet (ignoring the header) or an std::error::Error instance:
+ fn read(&self, buf: &mut [u8], offset: usize) -> Result<usize, Self::Error>;
+}
+
+pub trait MTU: Send + Sync + Clone + 'static {
+ /// Returns the MTU of the device
+ ///
+ /// This function needs to be efficient (called for every read).
+ /// The goto implementation strategy is to .load an atomic variable,
+ /// then use e.g. netlink to update the variable in a separate thread.
+ ///
+ /// # Returns
+ ///
+ /// The MTU of the interface in bytes
+ fn mtu(&self) -> usize;
+}
+
+pub trait Tun: Send + Sync + 'static {
+ type Writer: Writer;
+ type Reader: Reader;
+ type MTU: MTU;
+ type Error: Error;
+}
+
+/// On some platforms the application can create the TUN device itself.
+pub trait Platform: Tun {
+ fn create(name: &str) -> Result<(Vec<Self::Reader>, Self::Writer, Self::MTU), Self::Error>;
+}