From c78a9cb7778d88d4363b53089a5005f9547a8c48 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:27:47 +0100 Subject: [PATCH 1/2] docs(wireguard-broker): add docs and examples --- wireguard-broker/src/api/client.rs | 187 ++++++++++++++++++++ wireguard-broker/src/api/config.rs | 20 ++- wireguard-broker/src/api/mod.rs | 6 + wireguard-broker/src/api/msgs.rs | 83 +++++++++ wireguard-broker/src/api/server.rs | 94 +++++++++- wireguard-broker/src/bin/priviledged.rs | 22 +++ wireguard-broker/src/bin/socket_handler.rs | 9 + wireguard-broker/src/brokers/mio_client.rs | 93 ++++++++++ wireguard-broker/src/brokers/native_unix.rs | 119 +++++++++++++ wireguard-broker/src/brokers/netlink.rs | 52 ++++++ wireguard-broker/src/lib.rs | 72 +++++++- 11 files changed, 751 insertions(+), 6 deletions(-) diff --git a/wireguard-broker/src/api/client.rs b/wireguard-broker/src/api/client.rs index 73dbb06..6e43953 100644 --- a/wireguard-broker/src/api/client.rs +++ b/wireguard-broker/src/api/client.rs @@ -1,3 +1,41 @@ +//! Client implementation for the WireGuard broker protocol. +//! +//! This module provides a client implementation that communicates with a WireGuard broker server +//! using a binary protocol. The client handles serialization and deserialization of messages, +//! error handling, and the core interaction flow. +//! +//! # Examples +//! +//! ``` +//! use rosenpass_wireguard_broker::api::client::{BrokerClient, BrokerClientIo}; +//! #[derive(Debug)] +//! struct MyIo; +//! +//! impl BrokerClientIo for MyIo { +//! type SendError = std::io::Error; +//! type RecvError = std::io::Error; +//! +//! fn send_msg(&mut self, buf: &[u8]) -> Result<(), Self::SendError> { +//! // Implement sending logic +//! Ok(()) +//! } +//! +//! fn recv_msg(&mut self) -> Result, Self::RecvError> { +//! // Implement receiving logic +//! Ok(None) +//! } +//! } +//! +//! // Create client with custom IO implementation +//! let mut client = BrokerClient::new(MyIo); +//! assert!(client.poll_response().unwrap().is_none()); +//! ``` +//! +//! # Protocol +//! +//! The client implements a simple request-response protocol for setting WireGuard pre-shared keys. +//! Messages are serialized using a binary format defined in the [`crate::api::msgs`] module. + use std::{borrow::BorrowMut, fmt::Debug}; use crate::{ @@ -13,10 +51,13 @@ use super::{ msgs::{Envelope, SetPskResponse}, }; +/// Error type for polling responses from the broker server. #[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)] pub enum BrokerClientPollResponseError { + /// An IO error occurred while receiving the response #[error(transparent)] IoError(RecvError), + /// The received message was invalid or malformed #[error("Invalid message.")] InvalidMessage, } @@ -28,34 +69,52 @@ impl From for BrokerClientPollResponse } } +/// Helper function that wraps a receive error into a `BrokerClientPollResponseError::IoError` fn io_poller(e: RecvError) -> BrokerClientPollResponseError { BrokerClientPollResponseError::::IoError(e) } +/// Helper function that returns a `BrokerClientPollResponseError::InvalidMessage` error fn invalid_msg_poller() -> BrokerClientPollResponseError { BrokerClientPollResponseError::::InvalidMessage } +/// Error type for setting pre-shared keys through the broker client. #[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)] pub enum BrokerClientSetPskError { + /// Error encoding or decoding the message #[error("Error with encoding/decoding message")] MsgError, + /// Error in the broker configuration #[error("Network Broker Config error: {0}")] BrokerError(NetworkBrokerConfigErr), + /// IO error while sending the request #[error(transparent)] IoError(SendError), + /// Interface name exceeds maximum length #[error("Interface name out of bounds")] IfaceOutOfBounds, } +/// Trait defining the IO operations required by the broker client. +/// +/// Implementors must provide methods for sending and receiving binary messages. pub trait BrokerClientIo { + /// Error type returned by send operations type SendError; + /// Error type returned by receive operations type RecvError; + /// Send a binary message fn send_msg(&mut self, buf: &[u8]) -> Result<(), Self::SendError>; + /// Receive a binary message, returning None if no message is available fn recv_msg(&mut self) -> Result, Self::RecvError>; } +/// Client for interacting with a WireGuard broker server. +/// +/// The client handles the protocol-level communication with the server, +/// including message serialization and response handling. #[derive(Debug)] pub struct BrokerClient where @@ -68,18 +127,37 @@ impl BrokerClient where Io: BrokerClientIo + Debug, { + /// Creates a new `BrokerClient` with the given IO implementation. pub fn new(io: Io) -> Self { Self { io } } + /// Returns a reference to the underlying IO implementation. pub fn io(&self) -> &Io { &self.io } + /// Returns a mutable reference to the underlying IO implementation. pub fn io_mut(&mut self) -> &mut Io { &mut self.io } + /// Polls for a response from the broker server. + /// + /// This method attempts to receive and parse a SetPsk response message from the server. + /// If no message is available, returns `Ok(None)`. If a message is received, it is + /// parsed and validated before being returned as `Ok(Some(result))`. + /// + /// # Returns + /// - `Ok(Some(result))` if a valid response was received + /// - `Ok(None)` if no message was available + /// - `Err(BrokerClientPollResponseError)` if an error occurred during receiving or parsing + /// + /// # Errors + /// Returns an error if: + /// - An IO error occurs while receiving the message + /// - The received message is invalid or malformed + /// - The message type is incorrect pub fn poll_response( &mut self, ) -> Result, BrokerClientPollResponseError> { @@ -147,3 +225,112 @@ where Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + use msgs::{MsgType, SetPskError, SetPskResponseReturnCode}; + + // Mock IO implementation for testing + #[derive(Debug)] + struct MockIo { + recv_data: Vec, + } + + impl MockIo { + fn new() -> Self { + Self { + recv_data: Vec::new(), + } + } + + fn set_recv_data(&mut self, data: Option>) { + self.recv_data = data.unwrap_or_default(); + } + } + + impl BrokerClientIo for MockIo { + type SendError = std::io::Error; + type RecvError = std::io::Error; + + fn send_msg(&mut self, _buf: &[u8]) -> Result<(), Self::SendError> { + Ok(()) + } + + fn recv_msg(&mut self) -> Result, Self::RecvError> { + if self.recv_data.is_empty() { + Ok(None) + } else { + Ok(Some(&self.recv_data)) + } + } + } + + fn create_response_msg(return_code: u8) -> Vec { + let mut msg = vec![ + MsgType::SetPsk as u8, // msg_type + 0, + 0, + 0, // reserved bytes + ]; + msg.push(return_code); // return_code + msg + } + + #[test] + fn test_poll_response_no_message() { + let io = MockIo::new(); + let mut client = BrokerClient::new(io); + + assert_eq!(client.poll_response().unwrap(), None); + } + + #[test] + fn test_poll_response_success() { + let mut io = MockIo::new(); + io.set_recv_data(Some(create_response_msg( + SetPskResponseReturnCode::Success as u8, + ))); + let mut client = BrokerClient::new(io); + + assert_eq!(client.poll_response().unwrap(), Some(Ok(()))); + } + + #[test] + fn test_poll_response_no_such_peer() { + let mut io = MockIo::new(); + io.set_recv_data(Some(create_response_msg( + SetPskResponseReturnCode::NoSuchPeer as u8, + ))); + let mut client = BrokerClient::new(io); + + assert_eq!( + client.poll_response().unwrap(), + Some(Err(SetPskError::NoSuchPeer)) + ); + } + + #[test] + fn test_poll_response_invalid_message_type() { + let mut io = MockIo::new(); + io.set_recv_data(Some(vec![0xFF, 0, 0, 0, 0])); // Invalid message type + let mut client = BrokerClient::new(io); + + assert!(matches!( + client.poll_response(), + Err(BrokerClientPollResponseError::InvalidMessage) + )); + } + + #[test] + fn test_poll_response_invalid_return_code() { + let mut io = MockIo::new(); + io.set_recv_data(Some(create_response_msg(0xFF))); // Invalid return code + let mut client = BrokerClient::new(io); + + assert!(matches!( + client.poll_response(), + Err(BrokerClientPollResponseError::InvalidMessage) + )); + } +} diff --git a/wireguard-broker/src/api/config.rs b/wireguard-broker/src/api/config.rs index 97a36f4..3230768 100644 --- a/wireguard-broker/src/api/config.rs +++ b/wireguard-broker/src/api/config.rs @@ -1,3 +1,7 @@ +//! This module provides [NetworkBrokerConfig] for configuring a +//! [BrokerServer](crate::api::server::BrokerServer) and tooling to serialize and deserialize these +//! configurations. + use crate::{SerializedBrokerConfig, WG_KEY_LEN, WG_PEER_LEN}; use derive_builder::Builder; use rosenpass_secret_memory::{Public, Secret}; @@ -5,13 +9,19 @@ use rosenpass_secret_memory::{Public, Secret}; #[derive(Builder, Debug)] #[builder(pattern = "mutable")] //TODO: Use generics for iface, add additional params +/// Specifies a configuration for a [BrokerServer](crate::api::server::BrokerServer). pub struct NetworkBrokerConfig<'a> { + /// The interface for the [BrokerServer](crate::api::server::BrokerServer). pub iface: &'a str, + /// The peer identifier for the [BrokerServer](crate::api::server::BrokerServer). pub peer_id: &'a Public, + /// The pre-shared key for the [BrokerServer](crate::api::server::BrokerServer) and the + /// interface. pub psk: &'a Secret, } impl<'a> From> for SerializedBrokerConfig<'a> { + /// Transforms a [NetworkBrokerConfig] into a [SerializedBrokerConfig] meant for serialization. fn from(src: NetworkBrokerConfig<'a>) -> SerializedBrokerConfig<'a> { Self { interface: src.iface.as_bytes(), @@ -22,15 +32,23 @@ impl<'a> From> for SerializedBrokerConfig<'a> { } } +/// Error variants that can occur when loading a [NetworkBrokerConfig] from a +/// [SerializedBrokerConfig]. #[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)] pub enum NetworkBrokerConfigErr { + /// Error indicating that the interface specification could not be read correctly. #[error("Interface")] - Interface, + Interface, // TODO, give this a better name. } impl<'a> TryFrom> for NetworkBrokerConfig<'a> { type Error = NetworkBrokerConfigErr; + /// Tries to load a [NetworkBrokerConfig] from a [SerializedBrokerConfig]. + /// + /// # Errors + /// Returns a [NetworkBrokerConfigErr::Interface]-error when the interface description + /// can not be parsed correctly. fn try_from(value: SerializedBrokerConfig<'a>) -> Result { let iface = std::str::from_utf8(value.interface).map_err(|_| NetworkBrokerConfigErr::Interface)?; diff --git a/wireguard-broker/src/api/mod.rs b/wireguard-broker/src/api/mod.rs index fb60adb..9bb5b92 100644 --- a/wireguard-broker/src/api/mod.rs +++ b/wireguard-broker/src/api/mod.rs @@ -1,3 +1,9 @@ +//! This module implements the binary WireGuard broker protocol in the form of the [client::BrokerClient] +//! and the [server::BrokerServer]. +//! +//! Specifically, The protocol enables the client to tell the server to set a pre-shared key for a +//! wireguard interface. + pub mod client; pub mod config; pub mod msgs; diff --git a/wireguard-broker/src/api/msgs.rs b/wireguard-broker/src/api/msgs.rs index a5e4f60..4ed1fa3 100644 --- a/wireguard-broker/src/api/msgs.rs +++ b/wireguard-broker/src/api/msgs.rs @@ -1,11 +1,19 @@ +//! This module defines message formats for messages in the Wireguard Broker protocol as well as +//! helper structures like errors and conversion functions. + use std::str::{from_utf8, Utf8Error}; use zerocopy::{AsBytes, FromBytes, FromZeroes}; +/// The number of bytes reserved for overhead when packaging data. pub const ENVELOPE_OVERHEAD: usize = 1 + 3; + +/// The buffer size for request messages. pub const REQUEST_MSG_BUFFER_SIZE: usize = ENVELOPE_OVERHEAD + 32 + 32 + 1 + 255; +/// The buffer size for responses. pub const RESPONSE_MSG_BUFFER_SIZE: usize = ENVELOPE_OVERHEAD + 1; +/// Envelope for messages being passed around. #[repr(packed)] #[derive(AsBytes, FromBytes, FromZeroes)] pub struct Envelope { @@ -17,25 +25,43 @@ pub struct Envelope { pub payload: M, } +/// Message format for requests to set a pre-shared key. +/// # Example +/// #[repr(packed)] #[derive(AsBytes, FromBytes, FromZeroes)] pub struct SetPskRequest { + /// The pre-shared key. pub psk: [u8; 32], + /// The identifier of the peer. pub peer_id: [u8; 32], + /// The size for the interface pub iface_size: u8, // TODO: We should have variable length strings in lenses + /// The buffer for the interface. pub iface_buf: [u8; 255], } impl SetPskRequest { + /// Gets the interface specification as byte slice. pub fn iface_bin(&self) -> &[u8] { let len = self.iface_size as usize; &self.iface_buf[..len] } + /// Gets the interface specification as a `&str`. + /// + /// # Errors + /// Returns a [Utf8Error] if the interface specification isn't utf8 encoded. pub fn iface(&self) -> Result<&str, Utf8Error> { from_utf8(self.iface_bin()) } + /// Sets the interface specification to `iface`. No check is made whether `iface` is correctly + /// encoded as utf8. + /// + /// # Result + /// Returns [None] if `iface` is longer than 255 bytes. Otherwise, it returns + /// [Some(())](Some). pub fn set_iface_bin(&mut self, iface: &[u8]) -> Option<()> { (iface.len() < 256).then_some(())?; // Assert iface.len() < 256 @@ -47,17 +73,24 @@ impl SetPskRequest { Some(()) } + /// Sets the interface specification to `iface`. + /// + /// # Result + /// Returns [None] if `iface` is longer than 255 bytes. Otherwise, it returns + /// [Some(())](Some). pub fn set_iface(&mut self, iface: &str) -> Option<()> { self.set_iface_bin(iface.as_bytes()) } } +/// Message format for response to the set pre-shared key operation. #[repr(packed)] #[derive(AsBytes, FromBytes, FromZeroes)] pub struct SetPskResponse { pub return_code: u8, } +/// Error type for the errors that can occur when setting a pre-shared key. #[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)] pub enum SetPskError { #[error("The wireguard pre-shared-key assignment broker experienced an internal error.")] @@ -70,6 +103,12 @@ pub enum SetPskError { pub type SetPskResult = Result<(), SetPskError>; +/// The return codes and their meanings for the set psk response operation. +/// +/// [SetPskResponseReturnCode] is represented by by a single `u8` as required by the protocol. +/// +/// # Example +/// See [SetPskResponseReturnCode::try_from] for an example. #[repr(u8)] #[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] pub enum SetPskResponseReturnCode { @@ -85,6 +124,19 @@ pub struct InvalidSetPskResponseError; impl TryFrom for SetPskResponseReturnCode { type Error = InvalidSetPskResponseError; + /// Parse a [u8] as a [MsgType]. + /// + /// # Example + /// ``` + /// # use rosenpass_wireguard_broker::api::msgs::{InvalidSetPskResponseError, SetPskResponseReturnCode}; + /// let return_code: u8 = 0x00; // Usually specifically set or comes out of a message. + /// let res = SetPskResponseReturnCode::try_from(return_code); + /// assert!(res.is_ok()); + /// assert_eq!(res.unwrap(), SetPskResponseReturnCode::Success); + /// # Ok::<(), InvalidSetPskResponseError>(()) + /// ``` + /// # Errors + /// Returns a [InvalidSetPskResponseError] if `value` does not correspond to a known return code. fn try_from(value: u8) -> Result { use SetPskResponseReturnCode::*; match value { @@ -98,6 +150,9 @@ impl TryFrom for SetPskResponseReturnCode { } impl From for SetPskResult { + /// A [SetPskResult] can directly be deduced from a [SetPskResponseReturnCode]. + /// An [Ok] type is only returned if `value` is [SetPskResponseReturnCode::Success]. + /// Otherwise, an appropriate variant of [SetPskError] will be returned. fn from(value: SetPskResponseReturnCode) -> Self { use SetPskError as E; use SetPskResponseReturnCode as C; @@ -111,6 +166,7 @@ impl From for SetPskResult { } impl From for SetPskResponseReturnCode { + /// A [SetPskResponseReturnCode] can directly be deduced from a [SetPskResult]. fn from(value: SetPskResult) -> Self { use SetPskError as E; use SetPskResponseReturnCode as C; @@ -123,18 +179,45 @@ impl From for SetPskResponseReturnCode { } } +/// The types of messages supported by this crate. At the time of writing, this is only +/// the message to set a pre-shared key. +/// +/// [MsgType] is represented by a single `u8` as required by the protocol. +/// +/// # Example +/// It is usually used like this: +/// ``` +/// # use rosenpass_wireguard_broker::api::msgs::{InvalidMessageTypeError, MsgType}; +/// let typ: u8 = 0x01; // Usually specifically set or comes out of a message. +/// let typ = MsgType::try_from(typ)?; +/// let MsgType::SetPsk = typ; // Assert type. +/// # Ok::<(), InvalidMessageTypeError>(()) +/// ``` #[repr(u8)] #[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] pub enum MsgType { SetPsk = 0x01, } +/// Error indicating that an invalid [MsgType] was used. +/// This error is returned by [MsgType::try_from]. #[derive(Eq, PartialEq, Debug, Clone)] pub struct InvalidMessageTypeError; impl TryFrom for MsgType { type Error = InvalidMessageTypeError; + /// Parse a [u8] as a [MsgType]. + /// + /// # Example + /// ```rust + /// use rosenpass_wireguard_broker::api::msgs::MsgType; + /// let msg_type = MsgType::try_from(0x01); + /// assert!(msg_type.is_ok()); + /// assert_eq!(msg_type.unwrap(), MsgType::SetPsk); + /// ``` + /// # Errors + /// Returns an [InvalidMessageTypeError] if `value` does not correspond to a valid [MsgType]. fn try_from(value: u8) -> Result { match value { 0x01 => Ok(MsgType::SetPsk), diff --git a/wireguard-broker/src/api/server.rs b/wireguard-broker/src/api/server.rs index da3c2fe..1881cb3 100644 --- a/wireguard-broker/src/api/server.rs +++ b/wireguard-broker/src/api/server.rs @@ -1,3 +1,10 @@ +//! Server implementation for the WireGuard broker protocol. +//! +//! This module provides a server implementation that communicates with WireGuard broker clients +//! using a binary protocol. The server handles serialization and deserialization of messages, +//! error handling, and the core interaction flow. +//! Specifically, it handles requests to set a pre-shared key for a wireguard interface. + use std::borrow::BorrowMut; use rosenpass_secret_memory::{Public, Secret}; @@ -7,12 +14,16 @@ use crate::WireGuardBroker; use super::config::{NetworkBrokerConfigBuilder, NetworkBrokerConfigErr}; +/// Error variants for the [BrokerServer]. #[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)] pub enum BrokerServerError { + /// Indicates that an unknown request type was encountered. #[error("No such request type: {}", .0)] NoSuchRequestType(u8), + /// Indicates that an invalid message was sent. #[error("Invalid message received.")] InvalidMessage, + /// Indicates an error when configuration the network broker. #[error("Network Broker Config error: {0}")] BrokerError(NetworkBrokerConfigErr), } @@ -24,11 +35,18 @@ impl From for BrokerServerError { } } +/// The broker server. It requires an inner [WireGuardBroker] and an error type such +/// that the [msgs::SetPskError] implements [From] for the error type. +/// # Type Parameters +/// - `Err`: The used error type. Must be chosen such that [msgs::SetPskError] implements +/// [`From`](From) +///- `Inner`: A [WireGuardBroker]-type parametrized with `Err`. pub struct BrokerServer where Inner: WireGuardBroker, msgs::SetPskError: From, { + /// The inner [WireGuardBroker]. inner: Inner, } @@ -38,10 +56,17 @@ where msgs::SetPskError: From, Err: std::fmt::Debug, { + /// Creates a new [BrokerServer] from a [WireGuardBroker]. pub fn new(inner: Inner) -> Self { Self { inner } } + /// Processes a message (at the moment only setting the pre-shared key is supported) + /// and takes the appropriate actions. + /// + /// # Errors + /// - [BrokerServerError::InvalidMessage] if the message is not properly formatted or refers to + /// an unsupported message type. pub fn handle_message( &mut self, req: &[u8], @@ -54,21 +79,27 @@ where let msgs::MsgType::SetPsk = typ; // Assert type let req = zerocopy::Ref::<&[u8], Envelope>::new(req) - .ok_or(BrokerServerError::InvalidMessage)?; + .ok_or(InvalidMessage)?; let mut res = zerocopy::Ref::<&mut [u8], Envelope>::new(res) - .ok_or(BrokerServerError::InvalidMessage)?; + .ok_or(InvalidMessage)?; res.msg_type = msgs::MsgType::SetPsk as u8; self.handle_set_psk(&req.payload, &mut res.payload)?; Ok(res.bytes().len()) } + /// Sets the pre-shared key for the interface identified in `req` to the pre-shared key + /// specified in `req`. + /// + /// # Errors + /// - [InvalidMessage](BrokerServerError::InvalidMessage) if the `iface` specified in `req` is + /// longer than 255 bytes or not correctly encoded in utf8. fn handle_set_psk( &mut self, req: &SetPskRequest, res: &mut SetPskResponse, ) -> Result<(), BrokerServerError> { - // Using unwrap here since lenses can not return fixed-size arrays + // Using unwrap here since lenses can not return fixed-size arrays. // TODO: Slices should give access to fixed size arrays let peer_id = Public::from_slice(&req.peer_id); let psk = Secret::from_slice(&req.psk); @@ -95,3 +126,60 @@ where Ok(()) } } + +// We can only include this test if this feature is enabled, because otherwise +// brokers::netlink::SetPskError is not defined. +#[cfg(all(feature = "experiment_api", target_os = "linux"))] +#[cfg(test)] +mod tests { + use crate::api::msgs; + use crate::api::msgs::{Envelope, SetPskRequest}; + use crate::api::server::BrokerServer; + use crate::brokers::netlink::SetPskError; + use crate::{SerializedBrokerConfig, WireGuardBroker}; + use rosenpass_secret_memory::{secret_policy_use_only_malloc_secrets, Secret}; + use zerocopy::AsBytes; + + #[derive(Debug, Clone)] + struct MockWireGuardBroker { + psk: Secret<32>, + } + + impl WireGuardBroker for MockWireGuardBroker { + type Error = SetPskError; + + fn set_psk(&mut self, config: SerializedBrokerConfig<'_>) -> Result<(), Self::Error> { + self.psk = config.psk.clone(); + Ok(()) + } + } + + #[test] + fn test_broker_server() { + secret_policy_use_only_malloc_secrets(); + let mock_broker = MockWireGuardBroker { + psk: Secret::zero(), + }; + let mut broker_server = BrokerServer::new(mock_broker); + let mut iface_buf: [u8; 255] = [0; 255]; + // These are the utf encoded bytes of the string "wg0". + iface_buf[0] = 119; + iface_buf[1] = 103; + iface_buf[2] = 48; + let psk_req = SetPskRequest { + psk: [0; 32], + peer_id: [0; 32], + iface_size: 3, + iface_buf, + }; + let req: Envelope = Envelope { + msg_type: 0x01, // The only valid value. + reserved: [0, 0, 0], + payload: psk_req, + }; + let mut res: [u8; msgs::RESPONSE_MSG_BUFFER_SIZE] = [0; msgs::RESPONSE_MSG_BUFFER_SIZE]; + broker_server + .handle_message(req.as_bytes(), &mut res) + .unwrap(); + } +} diff --git a/wireguard-broker/src/bin/priviledged.rs b/wireguard-broker/src/bin/priviledged.rs index 7a42d21..1a8bbf3 100644 --- a/wireguard-broker/src/bin/priviledged.rs +++ b/wireguard-broker/src/bin/priviledged.rs @@ -1,3 +1,12 @@ +//! This module defines the Unix socket broker that interacts with the Linux-specific +//! WireGuard broker through a privileged process. +//! +//! It manages communication using length-prefixed +//! messages that are read from standard-input. +//! On each input message the process responds through its standard-output +//! +//! The functionality is only supported on Linux systems. + fn main() { #[cfg(target_os = "linux")] linux::main().unwrap(); @@ -8,20 +17,33 @@ fn main() { #[cfg(target_os = "linux")] pub mod linux { + //! Linux-specific implementation for the broker that communicates with the WireGuard broker. + use std::io::{stdin, stdout, Read, Write}; use rosenpass_wireguard_broker::api::msgs; use rosenpass_wireguard_broker::api::server::BrokerServer; use rosenpass_wireguard_broker::brokers::netlink as wg; + /// Represents errors that can occur during WireGuard broker operations #[derive(thiserror::Error, Debug)] pub enum BrokerAppError { + /// Wraps standard I/O errors that may occur during broker operations #[error(transparent)] IoError(#[from] std::io::Error), + + /// Wraps WireGuard connection errors #[error(transparent)] WgConnectError(#[from] wg::ConnectError), + + /// Wraps errors that occur when setting WireGuard Pre-Shared Keys (PSK) #[error(transparent)] WgSetPskError(#[from] wg::SetPskError), + + /// Indicates that a received message exceeds the maximum allowed size + /// + /// # Arguments + /// * `u64` - The size of the oversized message in bytes #[error("Oversized message {}; something about the request is fatally wrong", .0)] OversizedMessage(u64), } diff --git a/wireguard-broker/src/bin/socket_handler.rs b/wireguard-broker/src/bin/socket_handler.rs index 9f8c4e7..c3e2cba 100644 --- a/wireguard-broker/src/bin/socket_handler.rs +++ b/wireguard-broker/src/bin/socket_handler.rs @@ -1,3 +1,6 @@ +//! Provides an asynchronous Unix socket handler for managing connections between clients +//! and privileged WireGuard broker processes. + use std::process::Stdio; use tokio::io::{AsyncReadExt, AsyncWriteExt}; @@ -12,6 +15,7 @@ use clap::{ArgGroup, Parser}; use rosenpass_util::fd::claim_fd; use rosenpass_wireguard_broker::api::msgs; +/// Command-line arguments for configuring the socket handler #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] #[clap(group( @@ -45,11 +49,13 @@ struct Args { command: Vec, } +/// Represents a request to the broker with a channel for receiving the response struct BrokerRequest { reply_to: oneshot::Sender, request: Vec, } +/// Contains the broker's response data struct BrokerResponse { response: Vec, } @@ -87,6 +93,7 @@ async fn main() -> Result<()> { } } +/// Manages communication with the privileged broker process async fn direct_broker_process( mut queue: mpsc::Receiver, cmd: Vec, @@ -131,6 +138,7 @@ async fn direct_broker_process( } } +/// Accepts and handles incoming client connections async fn listen_for_clients(queue: mpsc::Sender, sock: UnixListener) -> Result<()> { loop { let (stream, _addr) = sock.accept().await?; @@ -145,6 +153,7 @@ async fn listen_for_clients(queue: mpsc::Sender, sock: UnixListen // NOTE: If loop can ever terminate we need to join the spawned tasks } +/// Handles individual client connections and message processing async fn on_accept(queue: mpsc::Sender, mut stream: UnixStream) -> Result<()> { let mut req_buf = Vec::new(); diff --git a/wireguard-broker/src/brokers/mio_client.rs b/wireguard-broker/src/brokers/mio_client.rs index 20eed09..e024eef 100644 --- a/wireguard-broker/src/brokers/mio_client.rs +++ b/wireguard-broker/src/brokers/mio_client.rs @@ -1,3 +1,52 @@ +//! Asynchronous WireGuard PSK broker client using mio for non-blocking I/O. +//! +//! This module provides a client implementation that communicates with a WireGuard broker +//! through Unix domain sockets using non-blocking I/O operations. It's designed to be used +//! in event-driven applications using the mio event framework. +//! +//! # Examples +//! +//! ```no_run +//! # use mio::net::UnixStream; +//! # use rosenpass_wireguard_broker::brokers::mio_client::MioBrokerClient; +//! # use rosenpass_wireguard_broker::{WireGuardBroker, WireguardBrokerMio}; +//! # use mio::{Events, Interest, Poll, Token}; +//! # use rosenpass_secret_memory::{Public, Secret}; +//! # use rosenpass_wireguard_broker::api::config::NetworkBrokerConfig; +//! # use rosenpass_wireguard_broker::SerializedBrokerConfig; +//! # fn main() -> Result<(), Box> { +//! let socket = UnixStream::connect("/path/to/broker.sock")?; +//! let mut client = MioBrokerClient::new(socket); +//! +//! // Set up mio polling +//! let mut poll = Poll::new()?; +//! let mut events = Events::with_capacity(128); +//! client.register(&poll.registry(), Token(0))?; +//! +//! // Prepare PSK configuration +//! let network_config = NetworkBrokerConfig { +//! iface: "wg0", +//! peer_id: &Public::zero(), // Replace with actual peer ID +//! psk: &Secret::zero(), // Replace with actual PSK +//! }; +//! +//! // Convert to serialized format and send +//! let config: SerializedBrokerConfig = network_config.into(); +//! client.set_psk(config)?; +//! +//! // Process responses in event loop +//! loop { +//! poll.poll(&mut events, None)?; +//! for event in &events { +//! if event.token() == Token(0) { +//! client.process_poll()?; +//! } +//! } +//! } +//! # Ok(()) +//! # } +//! ``` + use anyhow::{bail, Context}; use mio::Interest; use rosenpass_secret_memory::Secret; @@ -13,12 +62,44 @@ use crate::api::client::{ }; use crate::{SerializedBrokerConfig, WireGuardBroker, WireguardBrokerMio}; +/// WireGuard broker client using mio for non-blocking I/O operations. +/// +/// This client communicates with a WireGuard broker through a Unix domain socket, +/// using length-prefixed messages for communication. It supports both the basic +/// `WireGuardBroker` operations and non-blocking I/O through the +/// `WireguardBrokerMio` trait. +/// +/// # Examples +/// +/// ```no_run +/// use mio::net::UnixStream; +/// use rosenpass_wireguard_broker::brokers::mio_client::MioBrokerClient; +/// use rosenpass_wireguard_broker::{WireGuardBroker, SerializedBrokerConfig}; +/// use rosenpass_secret_memory::{Public, Secret}; +/// +/// # fn main() -> Result<(), Box> { +/// let socket = UnixStream::connect("/path/to/broker.sock")?; +/// let mut client = MioBrokerClient::new(socket); +/// +/// // Set a PSK +/// let config = SerializedBrokerConfig { +/// interface: "wg0".as_bytes(), +/// peer_id: &Public::zero(), // Replace with actual peer ID +/// psk: &Secret::zero(), // Replace with actual PSK +/// additional_params: &[], +/// }; +/// +/// client.set_psk(config)?; +/// # Ok(()) +/// # } +/// ``` #[derive(Debug)] pub struct MioBrokerClient { inner: BrokerClient, mio_token: Option, } +/// A buffer wrapper that provides secure memory for sensitive data. #[derive(Debug)] struct SecretBuffer(pub Secret); @@ -43,6 +124,10 @@ impl BorrowMut<[u8]> for SecretBuffer { type ReadBuffer = LengthPrefixDecoder>; type WriteBuffer = LengthPrefixEncoder>; +/// I/O implementation for the broker client using non-blocking operations. +/// +/// This type handles the low-level details of sending and receiving length-prefixed +/// messages over a Unix domain socket. #[derive(Debug)] struct MioBrokerClientIo { socket: mio::net::UnixStream, @@ -51,6 +136,10 @@ struct MioBrokerClientIo { } impl MioBrokerClient { + /// Creates a new client from a Unix domain socket. + /// + /// The socket should be connected to a WireGuard broker server that speaks + /// the same protocol. pub fn new(socket: mio::net::UnixStream) -> Self { let read_buffer = LengthPrefixDecoder::new(SecretBuffer::new()); let write_buffer = LengthPrefixEncoder::from_buffer(SecretBuffer::new()); @@ -66,6 +155,10 @@ impl MioBrokerClient { } } + /// Polls for and processes any pending responses from the broker. + /// + /// This method should be called when the socket becomes readable according + /// to mio events. fn poll(&mut self) -> anyhow::Result<()> { self.inner.io_mut().flush()?; diff --git a/wireguard-broker/src/brokers/native_unix.rs b/wireguard-broker/src/brokers/native_unix.rs index 1dad5df..fcfd4a4 100644 --- a/wireguard-broker/src/brokers/native_unix.rs +++ b/wireguard-broker/src/brokers/native_unix.rs @@ -1,3 +1,35 @@ +//! Native Unix implementation of the WireGuard PSK broker using the `wg` command-line tool. +//! +//! This module provides an implementation that works on Unix systems by executing the `wg` +//! command-line tool to set pre-shared keys. It requires the `wg` tool to be installed and +//! accessible in the system PATH. +//! +//! # Examples +//! +//! ```no_run +//! use rosenpass_secret_memory::{Public, Secret}; +//! use rosenpass_wireguard_broker::brokers::native_unix::{NativeUnixBroker, NativeUnixBrokerConfigBase}; +//! use rosenpass_wireguard_broker::{WireGuardBroker, WireguardBrokerCfg, WG_KEY_LEN, WG_PEER_LEN}; +//! +//! # fn main() -> Result<(), anyhow::Error> { +//! // Create a broker instance +//! let mut broker = NativeUnixBroker::new(); +//! +//! // Create configuration +//! let config = NativeUnixBrokerConfigBase { +//! interface: "wg0".to_string(), +//! peer_id: Public::zero(), // Replace with actual peer ID +//! extra_params: Vec::new(), +//! }; +//! +//! // Set PSK using the broker +//! let psk = Secret::::zero(); // Replace with actual PSK +//! let serialized_config = config.create_config(&psk); +//! broker.set_psk(serialized_config)?; +//! # Ok(()) +//! # } +//! ``` + use std::fmt::Debug; use std::process::{Command, Stdio}; use std::thread; @@ -12,9 +44,21 @@ use rosenpass_util::{b64::B64Display, file::StoreValueB64Writer}; use crate::{SerializedBrokerConfig, WireGuardBroker, WireguardBrokerCfg, WireguardBrokerMio}; use crate::{WG_KEY_LEN, WG_PEER_LEN}; +/// Maximum size of a base64-encoded WireGuard key in bytes const MAX_B64_KEY_SIZE: usize = WG_KEY_LEN * 5 / 3; +/// Maximum size of a base64-encoded WireGuard peer ID in bytes const MAX_B64_PEER_ID_SIZE: usize = WG_PEER_LEN * 5 / 3; +/// A WireGuard broker implementation that uses the native `wg` command-line tool. +/// +/// This broker executes the `wg` command to set pre-shared keys. It supports both synchronous +/// operations through the `WireGuardBroker` trait and asynchronous operations through the +/// `WireguardBrokerMio` trait. +/// +/// # Requirements +/// +/// - The `wg` command-line tool must be installed and in the system PATH +/// - The user running the broker must have sufficient permissions to execute `wg` commands #[derive(Debug)] pub struct NativeUnixBroker { mio_token: Option, @@ -110,16 +154,63 @@ impl WireguardBrokerMio for NativeUnixBroker { } } +/// Base configuration for the native Unix WireGuard broker. +/// +/// This configuration type is used to store persistent broker settings and create +/// serialized configurations for individual PSK operations. +/// +/// # Examples +/// +/// ``` +/// use rosenpass_wireguard_broker::brokers::native_unix::NativeUnixBrokerConfigBase; +/// use rosenpass_secret_memory::Public; +/// use rosenpass_wireguard_broker::WG_PEER_LEN; +/// +/// let config = NativeUnixBrokerConfigBase { +/// interface: "wg0".to_string(), +/// peer_id: Public::zero(), +/// extra_params: Vec::new(), +/// }; +/// ``` #[derive(Debug, Builder)] #[builder(pattern = "mutable")] pub struct NativeUnixBrokerConfigBase { + /// Name of the WireGuard interface (e.g., "wg0") pub interface: String, + /// Public key of the peer pub peer_id: Public, + /// Additional parameters to pass to the wg command #[builder(private)] pub extra_params: Vec, } impl NativeUnixBrokerConfigBaseBuilder { + /// Sets the peer ID from a base64-encoded string. + /// + /// # Arguments + /// + /// * `peer_id` - Base64-encoded peer public key + /// + /// # Examples + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// use rosenpass_wireguard_broker::brokers::native_unix::{NativeUnixBrokerConfigBaseBuilder}; + /// let mut peer_cfg = NativeUnixBrokerConfigBaseBuilder::default(); + /// // set peer id to [48;32] encoded as base64 + /// peer_cfg.peer_id_b64("MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA=")?; + /// peer_cfg.interface("wg0".to_string()); + /// peer_cfg.extra_params_ser(&vec![])?; + /// let peer_cfg = peer_cfg.build()?; + /// assert_eq!(peer_cfg.peer_id.value, [48u8;32]); + /// + /// let error = NativeUnixBrokerConfigBaseBuilder::default() + /// .peer_id_b64("MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA") // invalid base64 encoding + /// .err().unwrap(); + /// assert_eq!(error.to_string(), "Failed to parse peer id b64"); + /// # Ok(()) + /// # } + /// ``` pub fn peer_id_b64( &mut self, peer_id: &str, @@ -133,6 +224,29 @@ impl NativeUnixBrokerConfigBaseBuilder { Ok(self.peer_id(peer_id_b64)) } + /// Sets additional parameters for the wg command. + /// + /// Note: This function cannot fail as `Vec` is always serializable. + /// + /// # Examples + /// ``` + /// # fn main() -> Result<(), Box> { + /// use rosenpass_wireguard_broker::brokers::native_unix::NativeUnixBrokerConfigBaseBuilder; + /// + /// let mut peer_cfg = NativeUnixBrokerConfigBaseBuilder::default(); + /// // Set typical wireguard parameters + /// peer_cfg.interface("wg0".to_string()); + /// peer_cfg.peer_id_b64("Zm9v")?; + /// peer_cfg.extra_params_ser(&vec![ + /// "persistent-keepalive".to_string(), + /// "25".to_string(), + /// "allowed-ips".to_string(), + /// "10.0.0.2/32".to_string(), + /// ])?; + /// let peer_cfg = peer_cfg.build()?; + /// # Ok(()) + /// # } + /// ``` pub fn extra_params_ser( &mut self, extra_params: &Vec, @@ -157,12 +271,17 @@ impl WireguardBrokerCfg for NativeUnixBrokerConfigBase { } } +/// Runtime configuration for a single PSK operation. #[derive(Debug, Builder)] #[builder(pattern = "mutable")] pub struct NativeUnixBrokerConfig<'a> { + /// WireGuard interface name pub interface: &'a str, + /// Public key of the peer pub peer_id: &'a Public, + /// Pre-shared key to set pub psk: &'a Secret, + /// Additional wg command parameters pub extra_params: Vec, } diff --git a/wireguard-broker/src/brokers/netlink.rs b/wireguard-broker/src/brokers/netlink.rs index 2f4fa8f..71bfb11 100644 --- a/wireguard-broker/src/brokers/netlink.rs +++ b/wireguard-broker/src/brokers/netlink.rs @@ -1,4 +1,31 @@ #![cfg(target_os = "linux")] +//! Linux-specific WireGuard PSK broker implementation using netlink. +//! +//! This module provides direct kernel communication through netlink sockets for managing +//! WireGuard pre-shared keys. It's more efficient than the command-line implementation +//! but only available on Linux systems. +//! +//! # Examples +//! +//! ```no_run +//! use rosenpass_secret_memory::{Public, Secret}; +//! use rosenpass_wireguard_broker::{WireGuardBroker, SerializedBrokerConfig, WG_KEY_LEN, WG_PEER_LEN}; +//! use rosenpass_wireguard_broker::brokers::netlink::NetlinkWireGuardBroker; +//! +//! # fn main() -> Result<(), Box> { +//! let mut broker = NetlinkWireGuardBroker::new()?; +//! +//! let config = SerializedBrokerConfig { +//! interface: "wg0".as_bytes(), +//! peer_id: &Public::zero(), // Replace with actual peer ID +//! psk: &Secret::zero(), // Replace with actual PSK +//! additional_params: &[], +//! }; +//! +//! broker.set_psk(config)?; +//! # Ok(()) +//! # } +//! ``` use std::fmt::Debug; @@ -8,12 +35,14 @@ use crate::api::config::NetworkBrokerConfig; use crate::api::msgs; use crate::{SerializedBrokerConfig, WireGuardBroker}; +/// Error that can occur when connecting to the WireGuard netlink interface. #[derive(thiserror::Error, Debug)] pub enum ConnectError { #[error(transparent)] ConnectError(#[from] wg::err::ConnectError), } +/// Errors that can occur during netlink operations. #[derive(thiserror::Error, Debug)] pub enum NetlinkError { #[error(transparent)] @@ -22,6 +51,7 @@ pub enum NetlinkError { GetDevice(#[from] wg::err::GetDeviceError), } +/// Errors that can occur when setting a pre-shared key. #[derive(thiserror::Error, Debug)] pub enum SetPskError { #[error("The indicated wireguard interface does not exist")] @@ -55,11 +85,33 @@ impl From for SetPskMsgsError { } } +/// WireGuard broker implementation using Linux netlink sockets. +/// +/// This implementation communicates directly with the kernel through netlink sockets, +/// providing better performance than command-line based implementations. +/// +/// # Examples +/// +/// ```no_run +/// use rosenpass_wireguard_broker::brokers::netlink::NetlinkWireGuardBroker; +/// use rosenpass_wireguard_broker::WireGuardBroker; +/// # async fn example() -> Result<(), Box> { +/// let mut broker = NetlinkWireGuardBroker::new()?; +/// # Ok(()) +/// # } +/// ``` +/// +/// # Platform Support +/// +/// This implementation is only available on Linux systems and requires appropriate +/// permissions to use netlink sockets. pub struct NetlinkWireGuardBroker { sock: wg::WgSocket, } impl NetlinkWireGuardBroker { + /// Opens a netlink socket to the WireGuard kernel module + /// and returns a new netlink-based WireGuard broker. pub fn new() -> Result { let sock = wg::WgSocket::connect()?; Ok(Self { sock }) diff --git a/wireguard-broker/src/lib.rs b/wireguard-broker/src/lib.rs index ecb47bd..38fb192 100644 --- a/wireguard-broker/src/lib.rs +++ b/wireguard-broker/src/lib.rs @@ -1,38 +1,106 @@ +//! A broker interface for managing WireGuard pre-shared keys (PSK). +//! +//! This crate provides traits and implementations for interacting with WireGuard interfaces +//! to set pre-shared keys for peers. It supports different backend implementations including: +//! - Native Unix command-line interface +//! - Linux netlink interface +//! - Custom Unix socket protocol +//! +//! # Examples +//! +//! ```no_run +//! # use rosenpass_secret_memory::{Public, Secret}; +//! # use rosenpass_wireguard_broker::{WireGuardBroker, SerializedBrokerConfig, WG_KEY_LEN, WG_PEER_LEN}; +//! # use std::error::Error; +//! +//! # fn main() -> Result<(), Box> { +//! # rosenpass_secret_memory::policy::secret_policy_try_use_memfd_secrets(); +//! # let interface = "wg0"; +//! # let peer_id = Public::::zero(); +//! # let psk = Secret::::zero(); +//! +//! // Create a native Unix broker +//! let mut broker = rosenpass_wireguard_broker::brokers::native_unix::NativeUnixBroker::new(); +//! +//! // Configure and set PSK +//! let config = SerializedBrokerConfig { +//! interface: interface.as_bytes(), +//! peer_id: &peer_id, +//! psk: &psk, +//! additional_params: &[], +//! }; +//! +//! broker.set_psk(config)?; +//! # Ok(()) +//! # } +//! ``` + use rosenpass_secret_memory::{Public, Secret}; use std::fmt::Debug; +/// Length of a WireGuard key in bytes pub const WG_KEY_LEN: usize = 32; + +/// Length of a WireGuard peer ID in bytes pub const WG_PEER_LEN: usize = 32; + +/// Core trait for WireGuard PSK brokers. +/// +/// This trait defines the basic interface for setting pre-shared keys (PSK) on WireGuard interfaces. +/// Implementations handle the actual communication with WireGuard, whether through command-line tools, +/// netlink, or other mechanisms. pub trait WireGuardBroker: Debug { + /// The error type returned by broker operations type Error; + + /// Set a pre-shared key for a WireGuard peer fn set_psk(&mut self, config: SerializedBrokerConfig<'_>) -> Result<(), Self::Error>; } +/// Configuration trait for WireGuard PSK brokers. +/// +/// This trait allows creation of broker configurations from a PSK and implementation-specific +/// configuration data. pub trait WireguardBrokerCfg: Debug { + /// Creates a serialized broker configuration from this config and a specific PSK fn create_config<'a>(&'a self, psk: &'a Secret) -> SerializedBrokerConfig<'a>; } +/// Serialized configuration for WireGuard PSK operations. #[derive(Debug)] pub struct SerializedBrokerConfig<'a> { + /// The WireGuard interface name as UTF-8 bytes pub interface: &'a [u8], + /// The public key of the peer pub peer_id: &'a Public, + /// The pre-shared key to set pub psk: &'a Secret, + /// Additional implementation-specific parameters pub additional_params: &'a [u8], } +/// Extension trait for mio integration with WireGuard brokers. +/// +/// This trait extends the basic `WireGuardBroker` functionality with asynchronous I/O +/// operations using the mio event framework. pub trait WireguardBrokerMio: WireGuardBroker { + /// The error type for mio operations type MioError; - /// Register interested events for mio::Registry + + /// Register the broker with a mio Registry for event notifications fn register( &mut self, registry: &mio::Registry, token: mio::Token, ) -> Result<(), Self::MioError>; + + /// Get the mio token associated with this broker, if any fn mio_token(&self) -> Option; - /// Run after a mio::poll operation + /// Process events after a mio poll operation fn process_poll(&mut self) -> Result<(), Self::MioError>; + /// Unregister the broker from a mio Registry fn unregister(&mut self, registry: &mio::Registry) -> Result<(), Self::MioError>; } From 0d7dd99d96aa236b6f54ad10ed6663379e429a97 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:03:39 +0100 Subject: [PATCH 2/2] test(wireguard-broker): Add smoketest and doc-tests for wiregaurd broker --- wireguard-broker/src/api/server.rs | 8 ++-- wireguard-broker/src/brokers/netlink.rs | 55 +++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/wireguard-broker/src/api/server.rs b/wireguard-broker/src/api/server.rs index 1881cb3..9a55b3d 100644 --- a/wireguard-broker/src/api/server.rs +++ b/wireguard-broker/src/api/server.rs @@ -78,10 +78,10 @@ where let typ = msgs::MsgType::try_from(*typ)?; let msgs::MsgType::SetPsk = typ; // Assert type - let req = zerocopy::Ref::<&[u8], Envelope>::new(req) - .ok_or(InvalidMessage)?; - let mut res = zerocopy::Ref::<&mut [u8], Envelope>::new(res) - .ok_or(InvalidMessage)?; + let req = + zerocopy::Ref::<&[u8], Envelope>::new(req).ok_or(InvalidMessage)?; + let mut res = + zerocopy::Ref::<&mut [u8], Envelope>::new(res).ok_or(InvalidMessage)?; res.msg_type = msgs::MsgType::SetPsk as u8; self.handle_set_psk(&req.payload, &mut res.payload)?; diff --git a/wireguard-broker/src/brokers/netlink.rs b/wireguard-broker/src/brokers/netlink.rs index 71bfb11..ef8c181 100644 --- a/wireguard-broker/src/brokers/netlink.rs +++ b/wireguard-broker/src/brokers/netlink.rs @@ -11,8 +11,9 @@ //! use rosenpass_secret_memory::{Public, Secret}; //! use rosenpass_wireguard_broker::{WireGuardBroker, SerializedBrokerConfig, WG_KEY_LEN, WG_PEER_LEN}; //! use rosenpass_wireguard_broker::brokers::netlink::NetlinkWireGuardBroker; +//! # use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets; +//! # secret_policy_use_only_malloc_secrets(); //! -//! # fn main() -> Result<(), Box> { //! let mut broker = NetlinkWireGuardBroker::new()?; //! //! let config = SerializedBrokerConfig { @@ -23,8 +24,7 @@ //! }; //! //! broker.set_psk(config)?; -//! # Ok(()) -//! # } +//! # Ok::<(), anyhow::Error>(()) //! ``` use std::fmt::Debug; @@ -62,18 +62,41 @@ pub enum SetPskError { NetlinkError(#[from] NetlinkError), } +/// # Example +/// ``` +/// # use wireguard_uapi::err::NlError; +/// # use wireguard_uapi::linux::err::SetDeviceError; +/// use rosenpass_wireguard_broker::brokers::netlink::SetPskError; +/// let set_device_error: SetDeviceError = SetDeviceError::NlError(NlError::Msg("test-error".to_string())); +/// let set_psk_error: SetPskError = set_device_error.into(); +/// ``` impl From for SetPskError { fn from(err: wg::err::SetDeviceError) -> Self { NetlinkError::from(err).into() } } +/// # Example +/// ``` +/// # use wireguard_uapi::err::NlError; +/// # use wireguard_uapi::linux::err::GetDeviceError; +/// # use rosenpass_wireguard_broker::brokers::netlink::SetPskError; +/// let get_device_error: GetDeviceError = GetDeviceError::NlError(NlError::Msg("test-error".to_string())); +/// let set_psk_error: SetPskError = get_device_error.into(); +/// ``` impl From for SetPskError { fn from(err: wg::err::GetDeviceError) -> Self { NetlinkError::from(err).into() } } +/// # Example +/// ``` +/// use rosenpass_wireguard_broker::api::msgs::SetPskError as SetPskMsgsError; +/// use rosenpass_wireguard_broker::brokers::netlink::SetPskError as SetPskNetlinkError; +/// let set_psk_nlink_error: SetPskNetlinkError = SetPskNetlinkError::NoSuchInterface; +/// let set_psk_msgs_error = SetPskMsgsError::from(set_psk_nlink_error); +/// ``` use msgs::SetPskError as SetPskMsgsError; use SetPskError as SetPskNetlinkError; impl From for SetPskMsgsError { @@ -92,7 +115,7 @@ impl From for SetPskMsgsError { /// /// # Examples /// -/// ```no_run +/// ``` /// use rosenpass_wireguard_broker::brokers::netlink::NetlinkWireGuardBroker; /// use rosenpass_wireguard_broker::WireGuardBroker; /// # async fn example() -> Result<(), Box> { @@ -164,3 +187,27 @@ impl WireGuardBroker for NetlinkWireGuardBroker { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + use rosenpass_secret_memory::{secret_policy_use_only_malloc_secrets, Public, Secret}; + #[test] + fn smoke_test() -> Result<(), Box> { + secret_policy_use_only_malloc_secrets(); + let result = NetlinkWireGuardBroker::new(); + assert!(result.is_ok()); + let mut broker = result.unwrap(); + let peer_id = Public::zero(); + let psk = Secret::zero(); + let config: SerializedBrokerConfig = NetworkBrokerConfig { + iface: "wg0", + peer_id: &peer_id, + psk: &psk, + } + .into(); + let result = broker.set_psk(config); + assert!(result.is_err()); + Ok(()) + } +}