router/src/tunnels/mod.rs

135 lines
3.1 KiB
Rust

use once_cell::sync::OnceCell;
use sled::{Db, Tree};
static TUNNELS_TREE: OnceCell<Tree> = OnceCell::new();
fn tunnels_tree(db: &Db) -> &'static Tree {
TUNNELS_TREE.get_or_init(|| db.open_tree("tunnels").unwrap())
}
mod wireguard;
#[derive(serde::Deserialize)]
#[serde(tag = "type")]
pub(crate) enum NewTunnel {
Wireguard(wireguard::NewInterface),
}
#[derive(serde::Deserialize)]
pub(crate) struct NewPeer {
tunnel_id: String,
peer: NewPeerKind,
}
#[derive(serde::Deserialize)]
#[serde(tag = "type")]
pub(crate) enum NewPeerKind {
Wireguard(wireguard::Peer),
}
#[derive(serde::Deserialize, serde::Serialize)]
#[serde(tag = "type")]
pub(crate) enum Tunnel {
Wireguard(wireguard::Interface),
}
#[derive(serde::Deserialize, serde::Serialize)]
pub(crate) struct Peer {
tunnel_id: String,
peer: PeerKind,
}
#[derive(serde::Deserialize, serde::Serialize)]
#[serde(tag = "type")]
pub(crate) enum PeerKind {
Wireguard(wireguard::Peer),
}
pub(crate) async fn add_peer(db: &Db, new_peer: NewPeer) -> Result<Peer, anyhow::Error> {
let tree = tunnels_tree(db);
let tunnel_bytes = tree
.get(new_peer.tunnel_id.as_bytes())?
.ok_or_else(|| anyhow::anyhow!("Missing tunnel"))?;
let tunnel: Tunnel = serde_json::from_slice(&tunnel_bytes)?;
let peer = match (new_peer.peer, tunnel) {
(NewPeerKind::Wireguard(peer), Tunnel::Wireguard(interface)) => {
wireguard::add_peer(&interface, &peer).await?;
Peer {
tunnel_id: new_peer.tunnel_id,
peer: PeerKind::Wireguard(peer),
}
} // _ => return Err(anyhow::anyhow!("Peer kind mismatch")),
};
let v = serde_json::to_vec(&peer)?;
loop {
let id = db.generate_id()?;
let peer_id = peer_id(&peer.tunnel_id, id);
if tree
.compare_and_swap(
peer_id.as_bytes(),
None as Option<Vec<u8>>,
Some(v.as_slice()),
)?
.is_ok()
{
break;
}
}
tree.flush_async().await?;
Ok(peer)
}
pub(crate) async fn create(new_tunnel: NewTunnel) -> Result<Tunnel, anyhow::Error> {
match new_tunnel {
NewTunnel::Wireguard(new_interface) => {
let interface = wireguard::generate_config(new_interface).await?;
wireguard::apply(&interface).await?;
Ok(Tunnel::Wireguard(interface))
}
}
}
pub(crate) async fn save(db: &Db, tunnel: &Tunnel) -> Result<(), anyhow::Error> {
let tree = tunnels_tree(db);
let v = serde_json::to_vec(tunnel)?;
loop {
let id = db.generate_id()?;
let tunnel_id = tunnel_id(id);
if tree
.compare_and_swap(
tunnel_id.as_bytes(),
None as Option<Vec<u8>>,
Some(v.as_slice()),
)?
.is_ok()
{
break;
}
}
tree.flush_async().await?;
Ok(())
}
fn tunnel_id(id: u64) -> String {
format!("tunnel-{}", id)
}
fn peer_id(tunnel_id: &str, id: u64) -> String {
format!("{}/peers/{}", tunnel_id, id)
}