135 lines
3.1 KiB
Rust
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)
|
|
}
|