Files
wireguard-rs/src/configuration/config.rs
Mathias Hall-Andersen dcd567c08f Squashed commit of the following:
commit 1e26a0bef44e65023a97a16ecf3b123e688d19f7
Author: Mathias Hall-Andersen <mathias@hall-andersen.dk>
Date:   Sat Feb 1 14:36:50 2020 +0100

    Initial version of sticky sockets for Linux

commit 605cc656ad235d09ba6cd12d03dee2c5e0a9a80a
Author: Mathias Hall-Andersen <mathias@hall-andersen.dk>
Date:   Thu Jan 30 14:57:00 2020 +0100

    Clear src when sendmsg fails with EINVAL

commit dffd2b228af70f681e2a161642bbdaa348419bf3
Author: Mathias Hall-Andersen <mathias@hall-andersen.dk>
Date:   Sun Jan 26 14:01:28 2020 +0100

    Fix typoes

commit 2015663706fbe15ed1ac443a31de86b3e6c643c7
Author: Mathias Hall-Andersen <mathias@hall-andersen.dk>
Date:   Sun Jan 26 13:51:59 2020 +0100

    Restructure of public key -> peer state

    Restructured the mapping of public keys to peer state in the project.
    The handshake device is now generic over an opaque type,
    which enables it to be the sole place where public keys are mapped to
    the peer states.

    This gets rid of the "peer" map in the WireGuard devices
    and avoids having to include the public key in the handshake peer state.

commit bbcfaad4bcc5cf16bacdef0cefe7d29ba1519a23
Author: Mathias Hall-Andersen <mathias@hall-andersen.dk>
Date:   Fri Jan 10 21:10:27 2020 +0100

    Fixed bind6 also binding on IPv4

commit acbca236b70598c20c24de474690bcad883241d4
Author: Mathias Hall-Andersen <mathias@hall-andersen.dk>
Date:   Thu Jan 9 11:24:13 2020 +0100

    Work on sticky sockets
2020-02-01 14:39:19 +01:00

373 lines
10 KiB
Rust

use std::mem;
use std::net::{IpAddr, SocketAddr};
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex, MutexGuard};
use std::time::{Duration, SystemTime};
use x25519_dalek::{PublicKey, StaticSecret};
use super::udp::Owner;
use super::*;
/// 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.
///
/// Furthermore it forms the simpler interface for embedding WireGuard in other applications,
/// and hides the complex types of the implementation from the host application.
/// Describes a snapshot of the state of a peer
pub struct PeerState {
pub rx_bytes: u64,
pub tx_bytes: u64,
pub last_handshake_time: Option<(u64, u64)>,
pub public_key: PublicKey,
pub allowed_ips: Vec<(IpAddr, u32)>,
pub endpoint: Option<SocketAddr>,
pub persistent_keepalive_interval: u64,
pub preshared_key: [u8; 32], // 0^32 is the "default value" (though treated like any other psk)
}
pub struct WireGuardConfig<T: tun::Tun, B: udp::PlatformUDP>(Arc<Mutex<Inner<T, B>>>);
struct Inner<T: tun::Tun, B: udp::PlatformUDP> {
wireguard: WireGuard<T, B>,
port: u16,
bind: Option<B::Owner>,
fwmark: Option<u32>,
}
impl<T: tun::Tun, B: udp::PlatformUDP> WireGuardConfig<T, B> {
fn lock(&self) -> MutexGuard<Inner<T, B>> {
self.0.lock().unwrap()
}
}
impl<T: tun::Tun, B: udp::PlatformUDP> WireGuardConfig<T, B> {
pub fn new(wg: WireGuard<T, B>) -> WireGuardConfig<T, B> {
WireGuardConfig(Arc::new(Mutex::new(Inner {
wireguard: wg,
port: 0,
bind: None,
fwmark: None,
})))
}
}
impl<T: tun::Tun, B: udp::PlatformUDP> Clone for WireGuardConfig<T, B> {
fn clone(&self) -> Self {
WireGuardConfig(self.0.clone())
}
}
/// Exposed configuration interface
pub trait Configuration {
fn up(&self, mtu: usize);
fn down(&self);
fn start_listener(&self) -> Result<(), ConfigError>;
fn stop_listener(&self) -> Result<(), ConfigError>;
/// Updates the private key of the device
///
/// # Arguments
///
/// - `sk`: The new private key (or None, if the private key should be cleared)
fn set_private_key(&self, sk: Option<StaticSecret>);
/// Returns the private key of the device
///
/// # Returns
///
/// The private if set, otherwise None.
fn get_private_key(&self) -> Option<StaticSecret>;
/// Returns the protocol version of the device
///
/// # Returns
///
/// An integer indicating the protocol version
fn get_protocol_version(&self) -> usize;
fn set_listen_port(&self, port: u16) -> Result<(), ConfigError>;
/// Set the firewall mark (or similar, depending on platform)
///
/// # Arguments
///
/// - `mark`: The fwmark value
///
/// # Returns
///
/// An error if this operation is not supported by the underlying
/// "bind" implementation.
fn set_fwmark(&self, mark: Option<u32>) -> Result<(), ConfigError>;
/// Removes all peers from the device
fn replace_peers(&self);
/// Remove the peer from the
///
/// # Arguments
///
/// - `peer`: The public key of the peer to remove
///
/// # Returns
///
/// If the peer does not exists this operation is a noop
fn remove_peer(&self, peer: &PublicKey);
/// Adds a new peer to the device
///
/// # Arguments
///
/// - `peer`: The public key of the peer to add
///
/// # Returns
///
/// A bool indicating if the peer was added.
///
/// If the peer already exists this operation is a noop
fn add_peer(&self, peer: &PublicKey) -> bool;
/// Update the psk of a peer
///
/// # Arguments
///
/// - `peer`: The public key of the peer
/// - `psk`: The new psk or None if the psk should be unset
///
/// # Returns
///
/// An error if no such peer exists
fn set_preshared_key(&self, peer: &PublicKey, psk: [u8; 32]);
/// Update the endpoint of the
///
/// # Arguments
///
/// - `peer': The public key of the peer
/// - `psk`
fn set_endpoint(&self, peer: &PublicKey, addr: SocketAddr);
/// Update the endpoint of the
///
/// # Arguments
///
/// - `peer': The public key of the peer
/// - `psk`
fn set_persistent_keepalive_interval(&self, peer: &PublicKey, secs: u64);
/// Remove all allowed IPs from the peer
///
/// # Arguments
///
/// - `peer': The public key of the peer
///
/// # Returns
///
/// An error if no such peer exists
fn replace_allowed_ips(&self, peer: &PublicKey);
/// Add a new allowed subnet to the peer
///
/// # Arguments
///
/// - `peer`: The public key of the peer
/// - `ip`: Subnet mask
/// - `masklen`:
///
/// # Returns
///
/// An error if the peer does not exist
fn add_allowed_ip(&self, peer: &PublicKey, ip: IpAddr, masklen: u32);
fn get_listen_port(&self) -> Option<u16>;
/// Returns the state of all peers
///
/// # Returns
///
/// A list of structures describing the state of each peer
fn get_peers(&self) -> Vec<PeerState>;
fn get_fwmark(&self) -> Option<u32>;
}
impl<T: tun::Tun, B: udp::PlatformUDP> Configuration for WireGuardConfig<T, B> {
fn up(&self, mtu: usize) {
self.lock().wireguard.up(mtu);
}
fn down(&self) {
self.lock().wireguard.down();
}
fn get_fwmark(&self) -> Option<u32> {
self.lock().fwmark
}
fn set_private_key(&self, sk: Option<StaticSecret>) {
self.lock().wireguard.set_key(sk)
}
fn get_private_key(&self) -> Option<StaticSecret> {
self.lock().wireguard.get_sk()
}
fn get_protocol_version(&self) -> usize {
1
}
fn get_listen_port(&self) -> Option<u16> {
let st = self.lock();
log::trace!("Config, Get listen port, bound: {}", st.bind.is_some());
st.bind.as_ref().map(|bind| bind.get_port())
}
fn stop_listener(&self) -> Result<(), ConfigError> {
self.lock().bind = None;
Ok(())
}
fn start_listener(&self) -> Result<(), ConfigError> {
let mut cfg = self.lock();
// check if already listening
if cfg.bind.is_some() {
return Ok(());
}
// create new listener
let (mut readers, writer, mut owner) = match B::bind(cfg.port) {
Ok(r) => r,
Err(_) => {
return Err(ConfigError::FailedToBind);
}
};
// set fwmark
let _ = owner.set_fwmark(cfg.fwmark); // TODO: handle
// set writer on wireguard
cfg.wireguard.set_writer(writer);
// add readers
while let Some(reader) = readers.pop() {
cfg.wireguard.add_udp_reader(reader);
}
// create new UDP state
cfg.bind = Some(owner);
Ok(())
}
fn set_listen_port(&self, port: u16) -> Result<(), ConfigError> {
log::trace!("Config, Set listen port: {:?}", port);
// update port and take old bind
let old: Option<B::Owner> = {
let mut cfg = self.lock();
let old = mem::replace(&mut cfg.bind, None);
cfg.port = port;
old
};
// restart listener if bound
if old.is_some() {
self.start_listener()
} else {
Ok(())
}
// old bind is dropped, causing the file-descriptors to be released
}
fn set_fwmark(&self, mark: Option<u32>) -> Result<(), ConfigError> {
log::trace!("Config, Set fwmark: {:?}", mark);
match self.lock().bind.as_mut() {
Some(bind) => {
if bind.set_fwmark(mark).is_err() {
Err(ConfigError::IOError)
} else {
Ok(())
}
}
None => Ok(()),
}
}
fn replace_peers(&self) {
self.lock().wireguard.clear_peers();
}
fn remove_peer(&self, peer: &PublicKey) {
self.lock().wireguard.remove_peer(peer);
}
fn add_peer(&self, peer: &PublicKey) -> bool {
self.lock().wireguard.add_peer(*peer)
}
fn set_preshared_key(&self, peer: &PublicKey, psk: [u8; 32]) {
self.lock().wireguard.set_psk(*peer, psk);
}
fn set_endpoint(&self, peer: &PublicKey, addr: SocketAddr) {
if let Some(peer) = self.lock().wireguard.lookup_peer(peer) {
peer.router.set_endpoint(B::Endpoint::from_address(addr));
}
}
fn set_persistent_keepalive_interval(&self, peer: &PublicKey, secs: u64) {
if let Some(peer) = self.lock().wireguard.lookup_peer(peer) {
peer.set_persistent_keepalive_interval(secs);
}
}
fn replace_allowed_ips(&self, peer: &PublicKey) {
if let Some(peer) = self.lock().wireguard.lookup_peer(peer) {
peer.router.remove_allowed_ips();
}
}
fn add_allowed_ip(&self, peer: &PublicKey, ip: IpAddr, masklen: u32) {
if let Some(peer) = self.lock().wireguard.lookup_peer(peer) {
peer.router.add_allowed_ip(ip, masklen);
}
}
fn get_peers(&self) -> Vec<PeerState> {
let cfg = self.lock();
let peers = cfg.wireguard.list_peers();
let mut state = Vec::with_capacity(peers.len());
for p in peers {
// convert the system time to (secs, nano) since epoch
let last_handshake_time = (*p.walltime_last_handshake.lock()).and_then(|t| {
let duration = t
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or(Duration::from_secs(0));
Some((duration.as_secs(), duration.subsec_nanos() as u64))
});
if let Some(psk) = cfg.wireguard.get_psk(&p.pk) {
// extract state into PeerState
state.push(PeerState {
preshared_key: psk,
endpoint: p.router.get_endpoint(),
rx_bytes: p.rx_bytes.load(Ordering::Relaxed),
tx_bytes: p.tx_bytes.load(Ordering::Relaxed),
persistent_keepalive_interval: p.get_keepalive_interval(),
allowed_ips: p.router.list_allowed_ips(),
last_handshake_time,
public_key: p.pk,
})
}
}
state
}
}