mirror of https://git.zx2c4.com/wireguard-rs
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
326 lines
9.2 KiB
326 lines
9.2 KiB
use spin::Mutex; |
|
use std::net::{IpAddr, SocketAddr}; |
|
use std::sync::atomic::Ordering; |
|
use std::time::{Duration, SystemTime}; |
|
use x25519_dalek::{PublicKey, StaticSecret}; |
|
|
|
use super::*; |
|
use bind::Owner; |
|
|
|
/// 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. |
|
|
|
/// Describes a snapshot of the state of a peer |
|
pub struct PeerState { |
|
pub rx_bytes: u64, |
|
pub tx_bytes: u64, |
|
pub last_handshake_time_sec: u64, |
|
pub last_handshake_time_nsec: u64, |
|
pub public_key: PublicKey, |
|
pub allowed_ips: Vec<(IpAddr, u32)>, |
|
pub preshared_key: [u8; 32], // 0^32 is the "default value" |
|
} |
|
|
|
pub struct WireguardConfig<T: tun::Tun, B: bind::PlatformBind> { |
|
wireguard: Wireguard<T, B>, |
|
network: Mutex<Option<B::Owner>>, |
|
} |
|
|
|
impl<T: tun::Tun, B: bind::PlatformBind> WireguardConfig<T, B> { |
|
pub fn new(wg: Wireguard<T, B>) -> WireguardConfig<T, B> { |
|
WireguardConfig { |
|
wireguard: wg, |
|
network: Mutex::new(None), |
|
} |
|
} |
|
} |
|
|
|
/// Exposed configuration interface |
|
pub trait Configuration { |
|
/// 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: Option<u16>) -> Option<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>) -> Option<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]) -> Option<ConfigError>; |
|
|
|
/// Update the endpoint of the |
|
/// |
|
/// # Arguments |
|
/// |
|
/// - `peer': The public key of the peer |
|
/// - `psk` |
|
fn set_endpoint(&self, peer: &PublicKey, addr: SocketAddr) -> Option<ConfigError>; |
|
|
|
/// Update the endpoint of the |
|
/// |
|
/// # Arguments |
|
/// |
|
/// - `peer': The public key of the peer |
|
/// - `psk` |
|
fn set_persistent_keepalive_interval(&self, peer: &PublicKey, secs: u64) |
|
-> Option<ConfigError>; |
|
|
|
/// 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) -> Option<ConfigError>; |
|
|
|
/// 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 |
|
/// |
|
/// # Note: |
|
/// |
|
/// The API must itself sanitize the (ip, masklen) set: |
|
/// The ip should be masked to remove any set bits right of the first "masklen" bits. |
|
fn add_allowed_ip(&self, peer: &PublicKey, ip: IpAddr, masklen: u32) -> Option<ConfigError>; |
|
|
|
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: bind::PlatformBind> Configuration for WireguardConfig<T, B> { |
|
fn get_fwmark(&self) -> Option<u32> { |
|
self.network |
|
.lock() |
|
.as_ref() |
|
.and_then(|bind| bind.get_fwmark()) |
|
} |
|
|
|
fn set_private_key(&self, sk: Option<StaticSecret>) { |
|
self.wireguard.set_key(sk) |
|
} |
|
|
|
fn get_private_key(&self) -> Option<StaticSecret> { |
|
self.wireguard.get_sk() |
|
} |
|
|
|
fn get_protocol_version(&self) -> usize { |
|
1 |
|
} |
|
|
|
fn get_listen_port(&self) -> Option<u16> { |
|
self.network.lock().as_ref().map(|bind| bind.get_port()) |
|
} |
|
|
|
fn set_listen_port(&self, port: Option<u16>) -> Option<ConfigError> { |
|
let mut bind = self.network.lock(); |
|
|
|
// close the current listener |
|
*bind = None; |
|
|
|
// bind to new port |
|
if let Some(port) = port { |
|
// create new listener |
|
let (mut readers, writer, owner) = match B::bind(port) { |
|
Ok(r) => r, |
|
Err(_) => { |
|
return Some(ConfigError::FailedToBind); |
|
} |
|
}; |
|
|
|
// add readers/writer to wireguard |
|
self.wireguard.set_writer(writer); |
|
while let Some(reader) = readers.pop() { |
|
self.wireguard.add_reader(reader); |
|
} |
|
|
|
// create new UDP state |
|
*bind = Some(owner); |
|
} |
|
|
|
None |
|
} |
|
|
|
fn set_fwmark(&self, mark: Option<u32>) -> Option<ConfigError> { |
|
match self.network.lock().as_mut() { |
|
Some(bind) => { |
|
bind.set_fwmark(mark).unwrap(); // TODO: handle |
|
None |
|
} |
|
None => Some(ConfigError::NotListening), |
|
} |
|
} |
|
|
|
fn replace_peers(&self) { |
|
self.wireguard.clear_peers(); |
|
} |
|
|
|
fn remove_peer(&self, peer: &PublicKey) { |
|
self.wireguard.remove_peer(peer); |
|
} |
|
|
|
fn add_peer(&self, peer: &PublicKey) -> bool { |
|
self.wireguard.add_peer(*peer); |
|
false |
|
} |
|
|
|
fn set_preshared_key(&self, peer: &PublicKey, psk: [u8; 32]) -> Option<ConfigError> { |
|
if self.wireguard.set_psk(*peer, psk) { |
|
None |
|
} else { |
|
Some(ConfigError::NoSuchPeer) |
|
} |
|
} |
|
|
|
fn set_endpoint(&self, peer: &PublicKey, addr: SocketAddr) -> Option<ConfigError> { |
|
match self.wireguard.lookup_peer(peer) { |
|
Some(peer) => { |
|
peer.router.set_endpoint(B::Endpoint::from_address(addr)); |
|
None |
|
} |
|
None => Some(ConfigError::NoSuchPeer), |
|
} |
|
} |
|
|
|
fn set_persistent_keepalive_interval( |
|
&self, |
|
peer: &PublicKey, |
|
secs: u64, |
|
) -> Option<ConfigError> { |
|
match self.wireguard.lookup_peer(peer) { |
|
Some(peer) => { |
|
peer.set_persistent_keepalive_interval(secs); |
|
None |
|
} |
|
None => Some(ConfigError::NoSuchPeer), |
|
} |
|
} |
|
|
|
fn replace_allowed_ips(&self, peer: &PublicKey) -> Option<ConfigError> { |
|
match self.wireguard.lookup_peer(peer) { |
|
Some(peer) => { |
|
peer.router.remove_allowed_ips(); |
|
None |
|
} |
|
None => Some(ConfigError::NoSuchPeer), |
|
} |
|
} |
|
|
|
fn add_allowed_ip(&self, peer: &PublicKey, ip: IpAddr, masklen: u32) -> Option<ConfigError> { |
|
match self.wireguard.lookup_peer(peer) { |
|
Some(peer) => { |
|
peer.router.add_allowed_ip(ip, masklen); |
|
None |
|
} |
|
None => Some(ConfigError::NoSuchPeer), |
|
} |
|
} |
|
|
|
fn get_peers(&self) -> Vec<PeerState> { |
|
let peers = self.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 = (*p.walltime_last_handshake.lock()) |
|
.duration_since(SystemTime::UNIX_EPOCH) |
|
.unwrap_or(Duration::from_secs(0)); // any time before epoch is mapped to epoch |
|
|
|
if let Some(psk) = self.wireguard.get_psk(&p.pk) { |
|
// extract state into PeerState |
|
state.push(PeerState { |
|
preshared_key: psk, |
|
rx_bytes: p.rx_bytes.load(Ordering::Relaxed), |
|
tx_bytes: p.tx_bytes.load(Ordering::Relaxed), |
|
allowed_ips: p.router.list_allowed_ips(), |
|
last_handshake_time_nsec: last_handshake.subsec_nanos() as u64, |
|
last_handshake_time_sec: last_handshake.as_secs(), |
|
public_key: p.pk, |
|
}) |
|
} |
|
} |
|
state |
|
} |
|
}
|
|
|