diff --git a/crates/tsunagi/src/dataplane/mod.rs b/crates/tsunagi/src/dataplane/mod.rs index b357a06..83d676e 100644 --- a/crates/tsunagi/src/dataplane/mod.rs +++ b/crates/tsunagi/src/dataplane/mod.rs @@ -67,6 +67,20 @@ pub enum PluginError { Other(String), } +/// Scaffolding while the interface moves out of the plugin. +/// +/// The overlay interface belongs to the system level now, so a plugin has no +/// business failing because of it. This exists only for the callers that +/// have not been moved over yet and goes when the last of them does. +impl From for PluginError { + fn from(err: crate::overlay::OverlayError) -> Self { + match err { + crate::overlay::OverlayError::Unavailable(reason) => PluginError::Unavailable(reason), + crate::overlay::OverlayError::Other(reason) => PluginError::Other(reason), + } + } +} + /// A request a plugin makes of the agent that owns it. #[derive(Debug)] pub(crate) enum PluginRequest { diff --git a/crates/tsunagi/src/dataplane/wireguard/device.rs b/crates/tsunagi/src/dataplane/wireguard/device.rs index 45ff3e2..b6f4af9 100644 --- a/crates/tsunagi/src/dataplane/wireguard/device.rs +++ b/crates/tsunagi/src/dataplane/wireguard/device.rs @@ -43,8 +43,8 @@ use crate::identity::NetworkId; use super::keys::{WgPublicKey, WgSecretKey}; use super::overlay::overlay_address; -use super::packet::IpHeader; -use super::tun::TunDevice; +use crate::overlay::packet::IpHeader; +use crate::overlay::tun::TunDevice; use crate::state::Ipv4Range; /// How often WireGuard's own timers are driven. diff --git a/crates/tsunagi/src/dataplane/wireguard/mod.rs b/crates/tsunagi/src/dataplane/wireguard/mod.rs index 7c0f82f..22d98da 100644 --- a/crates/tsunagi/src/dataplane/wireguard/mod.rs +++ b/crates/tsunagi/src/dataplane/wireguard/mod.rs @@ -42,34 +42,34 @@ //! See `docs/wireguard.md` for the full picture. pub mod announcement; -pub mod config; pub mod device; pub mod keys; pub mod overlay; -pub mod packet; pub mod plugin; -pub mod provision; pub mod store; -pub mod tun; pub use crate::state::Ipv4Range; pub use announcement::{ValidatedAnnouncement, WgAnnouncement}; -pub use config::{Cidr, DEFAULT_INTERFACE_PREFIX, MAX_INTERFACE_NAME_LEN, interface_name}; -pub use device::{PeerHealth, PeerStats, PeerSummary, WireguardDevice}; -pub use keys::{WgPublicKey, WgSecretKey}; -pub use overlay::{OVERLAY_PREFIX_LEN, overlay_address, overlay_address_v4, overlay_prefix}; -pub use packet::IpHeader; -pub use plugin::{ - DEFAULT_MTU, MIN_MTU, NetworkOverview, PeerOverview, WIREGUARD_OVERHEAD, WIREGUARD_PROTOCOL, - WireguardConfig, WireguardPlugin, -}; -pub use provision::{ +// The interface, its addresses and how it is created belong to the system +// level now: one agent has one interface, and no protocol owns it. Re-exported +// here while callers are moved over. +pub use crate::overlay::provision::{ Changes, InterfacePlan, InterfaceProvisioner, InterfaceState, LinkKind, ManagedTunFactory, MockHost, MockProvisioner, Privilege, Provisioned, UnsupportedProvisioner, plan_changes, probe_net_admin, }; +pub use crate::overlay::{ + Cidr, DEFAULT_INTERFACE_PREFIX, IpHeader, MAX_INTERFACE_NAME_LEN, MemoryTun, MemoryTunFactory, + TunDevice, TunFactory, TunRequest, address_is_local, interface_name, +}; +pub use device::{PeerHealth, PeerStats, PeerSummary, WireguardDevice}; +pub use keys::{WgPublicKey, WgSecretKey}; +pub use overlay::{OVERLAY_PREFIX_LEN, overlay_address, overlay_address_v4, overlay_prefix}; +pub use plugin::{ + DEFAULT_MTU, MIN_MTU, NetworkOverview, PeerOverview, WIREGUARD_OVERHEAD, WIREGUARD_PROTOCOL, + WireguardConfig, WireguardPlugin, +}; pub use store::WgKeyStore; -pub use tun::{MemoryTun, MemoryTunFactory, TunDevice, TunFactory, TunRequest, address_is_local}; #[cfg(all(feature = "tun-device", target_os = "linux"))] -pub use provision::NetlinkProvisioner; +pub use crate::overlay::NetlinkProvisioner; diff --git a/crates/tsunagi/src/dataplane/wireguard/plugin.rs b/crates/tsunagi/src/dataplane/wireguard/plugin.rs index cdf1ef5..f082dbb 100644 --- a/crates/tsunagi/src/dataplane/wireguard/plugin.rs +++ b/crates/tsunagi/src/dataplane/wireguard/plugin.rs @@ -40,12 +40,12 @@ use crate::dataplane::{IpPlugin, PluginCapability, PluginContext, PluginError}; use crate::identity::NetworkId; use super::announcement::{ValidatedAnnouncement, WgAnnouncement}; -use super::config::{DEFAULT_INTERFACE_PREFIX, interface_name}; use super::device::{PeerSummary, WireguardDevice}; use super::keys::{WgPublicKey, WgSecretKey}; use super::overlay::{OVERLAY_PREFIX_LEN, overlay_address, overlay_prefix}; use super::store::WgKeyStore; -use super::tun::{TunFactory, TunRequest}; +use crate::overlay::config::{DEFAULT_INTERFACE_PREFIX, interface_name}; +use crate::overlay::tun::{TunFactory, TunRequest}; use crate::state::Ipv4Range; /// The protocol identifier this plugin announces. @@ -603,7 +603,7 @@ impl Worker { // kind that has been wrong here before. let missing_v4 = match (own_v4, interface.as_deref(), range) { (Some(address), Some(interface), Some(range)) - if !super::tun::address_is_local(IpAddr::V4(address)) => + if !crate::overlay::tun::address_is_local(IpAddr::V4(address)) => { let already = state_reported == Some(address); if let Some(state) = shared.networks.get_mut(&network) { diff --git a/crates/tsunagi/src/lib.rs b/crates/tsunagi/src/lib.rs index 45f5780..1b96c6b 100644 --- a/crates/tsunagi/src/lib.rs +++ b/crates/tsunagi/src/lib.rs @@ -44,6 +44,7 @@ pub mod error; pub mod identity; pub mod ipc; pub mod net; +pub mod overlay; pub mod proto; pub mod state; pub mod storage; diff --git a/crates/tsunagi/src/dataplane/wireguard/config.rs b/crates/tsunagi/src/overlay/config.rs similarity index 90% rename from crates/tsunagi/src/dataplane/wireguard/config.rs rename to crates/tsunagi/src/overlay/config.rs index de7eab4..fd6b762 100644 --- a/crates/tsunagi/src/dataplane/wireguard/config.rs +++ b/crates/tsunagi/src/overlay/config.rs @@ -11,10 +11,11 @@ use std::net::{IpAddr, Ipv6Addr}; -use crate::dataplane::PluginError; use crate::identity::NetworkId; +use crate::overlay::OverlayError; -use super::overlay::OVERLAY_HOST_PREFIX_LEN; +/// Prefix length of a single IPv6 host address. +const HOST_PREFIX_LEN: u8 = 128; /// Longest interface name Linux accepts, excluding the terminating NUL. pub const MAX_INTERFACE_NAME_LEN: usize = 15; @@ -33,13 +34,13 @@ pub struct Cidr { impl Cidr { /// Builds a CIDR, rejecting an impossible prefix length. - pub fn new(addr: IpAddr, prefix_len: u8) -> Result { + pub fn new(addr: IpAddr, prefix_len: u8) -> Result { let max = match addr { IpAddr::V4(_) => 32, IpAddr::V6(_) => 128, }; if prefix_len > max { - return Err(PluginError::Other(format!( + return Err(OverlayError::Other(format!( "prefix length /{prefix_len} is impossible for {addr}" ))); } @@ -50,7 +51,7 @@ impl Cidr { pub fn host(addr: Ipv6Addr) -> Self { Self { addr: IpAddr::V6(addr), - prefix_len: OVERLAY_HOST_PREFIX_LEN, + prefix_len: HOST_PREFIX_LEN, } } } @@ -66,9 +67,9 @@ impl std::fmt::Display for Cidr { /// The name is stable across restarts and short enough for the platform. Two /// agents on the same host in the same network must be given different /// prefixes, or they would derive the same name. -pub fn interface_name(prefix: &str, network: NetworkId) -> Result { +pub fn interface_name(prefix: &str, network: NetworkId) -> Result { if prefix.is_empty() { - return Err(PluginError::Other( + return Err(OverlayError::Other( "interface prefix must not be empty".into(), )); } @@ -76,12 +77,12 @@ pub fn interface_name(prefix: &str, network: NetworkId) -> Result= MAX_INTERFACE_NAME_LEN { - return Err(PluginError::Other(format!( + return Err(OverlayError::Other(format!( "interface prefix must be shorter than {MAX_INTERFACE_NAME_LEN} characters" ))); } diff --git a/crates/tsunagi/src/overlay/mod.rs b/crates/tsunagi/src/overlay/mod.rs new file mode 100644 index 0000000..ed31bd0 --- /dev/null +++ b/crates/tsunagi/src/overlay/mod.rs @@ -0,0 +1,54 @@ +//! The overlay interface: the one network interface an agent owns. +//! +//! One agent, one interface. It belongs to the system level rather than to +//! any protocol, and that is the whole point: a packet leaving it is routed +//! to whichever peer owns its destination address, over whichever protocol +//! currently has a link to that peer. Several protocols can be live at once +//! without arguing over who holds the address, because neither of them +//! holds it — the agent does. +//! +//! What lives here: +//! +//! * [`provision`] creates the interface and configures it, and removes it +//! again. Platform mechanics behind one decision function. +//! * [`tun`] is the packet interface itself, real or in memory. +//! * [`packet`] reads just enough of an IP header to route by it. +//! * [`config`] names interfaces and describes addresses. +//! +//! What does not live here: encryption, peer discovery, and how a packet +//! actually reaches another machine. Those belong to a protocol plugin, +//! which this level hands packets to and takes packets from. + +/// Why the overlay interface cannot be brought into the state asked for. +/// +/// Its own error rather than the plugin one it used to borrow: this level +/// owns the interface now, and a plugin failing is a different event from +/// the interface failing. +#[derive(Debug, thiserror::Error)] +#[non_exhaustive] +pub enum OverlayError { + /// The interface cannot be created or configured right now, with the + /// reason and, where there is one, what to do about it. + #[error("overlay unavailable: {0}")] + Unavailable(String), + /// Anything else. + #[error("{0}")] + Other(String), +} + +pub mod config; +pub mod packet; +pub mod provision; +pub mod tun; + +pub use config::{Cidr, DEFAULT_INTERFACE_PREFIX, MAX_INTERFACE_NAME_LEN, interface_name}; +pub use packet::IpHeader; +pub use provision::{ + Changes, InterfacePlan, InterfaceProvisioner, InterfaceState, LinkKind, ManagedTunFactory, + MockHost, MockProvisioner, Privilege, Provisioned, UnsupportedProvisioner, plan_changes, + probe_net_admin, +}; +pub use tun::{MemoryTun, MemoryTunFactory, TunDevice, TunFactory, TunRequest, address_is_local}; + +#[cfg(all(feature = "tun-device", target_os = "linux"))] +pub use provision::NetlinkProvisioner; diff --git a/crates/tsunagi/src/dataplane/wireguard/packet.rs b/crates/tsunagi/src/overlay/packet.rs similarity index 100% rename from crates/tsunagi/src/dataplane/wireguard/packet.rs rename to crates/tsunagi/src/overlay/packet.rs diff --git a/crates/tsunagi/src/dataplane/wireguard/provision/factory.rs b/crates/tsunagi/src/overlay/provision/factory.rs similarity index 96% rename from crates/tsunagi/src/dataplane/wireguard/provision/factory.rs rename to crates/tsunagi/src/overlay/provision/factory.rs index 4fd1fc9..8e30a07 100644 --- a/crates/tsunagi/src/dataplane/wireguard/provision/factory.rs +++ b/crates/tsunagi/src/overlay/provision/factory.rs @@ -9,14 +9,14 @@ use std::sync::Arc; use crate::BoxFuture; -use crate::dataplane::PluginError; +use crate::overlay::OverlayError; use super::super::config::Cidr; use super::super::tun::{TunDevice, TunFactory, TunRequest}; use super::{InterfacePlan, InterfaceProvisioner}; /// Turns a [`TunRequest`] into the plan for a host interface. -fn plan_for(request: &TunRequest) -> Result { +fn plan_for(request: &TunRequest) -> Result { let mut addresses = vec![Cidr::new(request.address.into(), request.prefix_len)?]; if let Some(address) = request.address_v4 { addresses.push(Cidr::new(address.into(), request.prefix_len_v4)?); @@ -54,7 +54,7 @@ impl TunFactory for ManagedTunFactory { fn create<'a>( &'a self, request: TunRequest, - ) -> BoxFuture<'a, Result, PluginError>> { + ) -> BoxFuture<'a, Result, OverlayError>> { Box::pin(async move { let plan = plan_for(&request)?; let provisioned = self.provisioner.reconcile(&plan).await?; @@ -67,7 +67,7 @@ impl TunFactory for ManagedTunFactory { // Reaching here would mean the interface already existed and // was held open by us, which cannot be true on the path that // creates a device. - PluginError::Other(format!( + OverlayError::Other(format!( "interface `{}` was reconciled but no device came back", plan.name )) @@ -75,7 +75,7 @@ impl TunFactory for ManagedTunFactory { }) } - fn reconfigure<'a>(&'a self, request: TunRequest) -> BoxFuture<'a, Result<(), PluginError>> { + fn reconfigure<'a>(&'a self, request: TunRequest) -> BoxFuture<'a, Result<(), OverlayError>> { Box::pin(async move { let plan = plan_for(&request)?; let provisioned = self.provisioner.reconcile(&plan).await?; diff --git a/crates/tsunagi/src/dataplane/wireguard/provision/linux.rs b/crates/tsunagi/src/overlay/provision/linux.rs similarity index 92% rename from crates/tsunagi/src/dataplane/wireguard/provision/linux.rs rename to crates/tsunagi/src/overlay/provision/linux.rs index 8bd2afd..d8b5497 100644 --- a/crates/tsunagi/src/dataplane/wireguard/provision/linux.rs +++ b/crates/tsunagi/src/overlay/provision/linux.rs @@ -50,7 +50,7 @@ use rtnetlink::packet_route::link::{InfoKind, LinkAttribute, LinkFlags, LinkInfo use rtnetlink::{LinkMessageBuilder, LinkUnspec}; use crate::BoxFuture; -use crate::dataplane::PluginError; +use crate::overlay::OverlayError; use super::super::config::Cidr; use super::super::tun::{TunDevice, TunRequest}; @@ -71,7 +71,7 @@ enum Command { Stop, } -type Reply = mpsc::Sender>; +type Reply = mpsc::Sender>; /// The configuration half of a reconciliation. struct Configure { @@ -97,17 +97,17 @@ pub struct NetlinkProvisioner { impl NetlinkProvisioner { /// Starts the netlink thread, after checking this process can use it. - pub fn new() -> Result { + pub fn new() -> Result { match probe_net_admin() { Privilege::Available => {} Privilege::Missing(reason) => { - return Err(PluginError::Unavailable(format!( + return Err(OverlayError::Unavailable(format!( "{reason}. {}", Privilege::how_to_grant(¤t_program()) ))); } Privilege::Unsupported => { - return Err(PluginError::Unavailable( + return Err(OverlayError::Unavailable( "interface management is not compiled in".to_string(), )); } @@ -118,7 +118,7 @@ impl NetlinkProvisioner { .name("tsunagi-netlink".to_string()) .spawn(move || netlink_thread(requests)) .map_err(|err| { - PluginError::Unavailable(format!("cannot start the netlink thread: {err}")) + OverlayError::Unavailable(format!("cannot start the netlink thread: {err}")) })?; Ok(Self { @@ -132,13 +132,13 @@ impl NetlinkProvisioner { fn call( &self, make: impl FnOnce(Reply) -> Command, - ) -> Result { + ) -> Result { let (reply_tx, reply_rx) = mpsc::channel(); self.commands .send(make(reply_tx)) - .map_err(|_| PluginError::Unavailable("the netlink thread has stopped".to_string()))?; + .map_err(|_| OverlayError::Unavailable("the netlink thread has stopped".to_string()))?; reply_rx.recv().map_err(|_| { - PluginError::Unavailable("the netlink thread stopped mid-request".to_string()) + OverlayError::Unavailable("the netlink thread stopped mid-request".to_string()) })? } @@ -150,7 +150,7 @@ impl NetlinkProvisioner { /// /// Synchronous on purpose: the capability guard is raised and lowered /// without an `await` in between, so it cannot outlive this thread. - fn create_device(&self, plan: &InterfacePlan) -> Result, PluginError> { + fn create_device(&self, plan: &InterfacePlan) -> Result, OverlayError> { let request = TunRequest::bare(plan.name.clone(), plan.mtu); let _guard = NetAdmin::acquire()?; super::super::tun::open_tun(&request) @@ -174,7 +174,7 @@ impl InterfaceProvisioner for NetlinkProvisioner { fn reconcile<'a>( &'a self, plan: &'a InterfacePlan, - ) -> BoxFuture<'a, Result> { + ) -> BoxFuture<'a, Result> { Box::pin(async move { let current = self.call(|reply| Command::Observe(plan.name.clone(), reply))?; let changes = plan_changes(¤t, plan, self.is_ours(&plan.name))?; @@ -224,7 +224,7 @@ impl InterfaceProvisioner for NetlinkProvisioner { }) } - fn remove<'a>(&'a self, name: &'a str) -> BoxFuture<'a, Result<(), PluginError>> { + fn remove<'a>(&'a self, name: &'a str) -> BoxFuture<'a, Result<(), OverlayError>> { Box::pin(async move { // Dropping the device is what removes the interface; the explicit // delete is only so it is gone by the time this returns rather @@ -271,10 +271,10 @@ fn netlink_thread(requests: mpsc::Receiver) { let message = format!("the netlink thread has no runtime: {err}"); match command { Command::Observe(_, reply) => { - let _ = reply.send(Err(PluginError::Unavailable(message))); + let _ = reply.send(Err(OverlayError::Unavailable(message))); } Command::Delete(_, reply) | Command::Configure(_, reply) => { - let _ = reply.send(Err(PluginError::Unavailable(message))); + let _ = reply.send(Err(OverlayError::Unavailable(message))); } Command::Stop => return, } @@ -311,12 +311,12 @@ fn netlink_thread(requests: mpsc::Receiver) { } /// Opens a netlink connection and runs one unit of work over it. -async fn with_netlink(work: impl FnOnce(rtnetlink::Handle) -> F) -> Result +async fn with_netlink(work: impl FnOnce(rtnetlink::Handle) -> F) -> Result where - F: std::future::Future>, + F: std::future::Future>, { let (connection, handle, _messages) = rtnetlink::new_connection() - .map_err(|err| PluginError::Unavailable(format!("cannot open netlink: {err}")))?; + .map_err(|err| OverlayError::Unavailable(format!("cannot open netlink: {err}")))?; let pump = tokio::spawn(connection); let result = work(handle).await; pump.abort(); @@ -361,7 +361,7 @@ fn link_kind(message: &LinkMessage, name: &str) -> LinkKind { } } -async fn observe(name: &str) -> Result { +async fn observe(name: &str) -> Result { with_netlink(|handle| async move { let mut links = handle.link().get().match_name(name.to_string()).execute(); let message = match links.try_next().await { @@ -373,7 +373,7 @@ async fn observe(name: &str) -> Result { if !std::path::Path::new(&format!("/sys/class/net/{name}")).exists() { return Ok(InterfaceState::absent()); } - return Err(PluginError::Unavailable(format!( + return Err(OverlayError::Unavailable(format!( "cannot read interface `{name}`: {err}" ))); } @@ -397,7 +397,7 @@ async fn observe(name: &str) -> Result { .set_link_index_filter(index) .execute(); while let Some(message) = stream.try_next().await.map_err(|err| { - PluginError::Unavailable(format!("cannot read the addresses of `{name}`: {err}")) + OverlayError::Unavailable(format!("cannot read the addresses of `{name}`: {err}")) })? { if let Some(cidr) = address_of(&message) && !is_link_local(cidr.addr) @@ -440,7 +440,7 @@ fn cidr(addr: IpAddr, prefix_len: u8) -> Option { Cidr::new(addr, prefix_len).ok() } -async fn delete_link(name: &str) -> Result<(), PluginError> { +async fn delete_link(name: &str) -> Result<(), OverlayError> { with_netlink(|handle| async move { let mut links = handle.link().get().match_name(name.to_string()).execute(); let index = match links.try_next().await { @@ -449,13 +449,13 @@ async fn delete_link(name: &str) -> Result<(), PluginError> { Ok(None) | Err(_) => return Ok(()), }; handle.link().del(index).execute().await.map_err(|err| { - PluginError::Unavailable(format!("cannot remove interface `{name}`: {err}")) + OverlayError::Unavailable(format!("cannot remove interface `{name}`: {err}")) }) }) .await } -async fn configure_link(configure: &Configure) -> Result<(), PluginError> { +async fn configure_link(configure: &Configure) -> Result<(), OverlayError> { let name = configure.name.as_str(); with_netlink(|handle| async move { let mut links = handle.link().get().match_name(name.to_string()).execute(); @@ -466,7 +466,7 @@ async fn configure_link(configure: &Configure) -> Result<(), PluginError> { .flatten() .map(|message| message.header.index) .ok_or_else(|| { - PluginError::Unavailable(format!( + OverlayError::Unavailable(format!( "interface `{name}` disappeared before it could be configured" )) })?; @@ -485,7 +485,7 @@ async fn configure_link(configure: &Configure) -> Result<(), PluginError> { .execute() .await .map_err(|err| { - PluginError::Unavailable(format!("cannot configure interface `{name}`: {err}")) + OverlayError::Unavailable(format!("cannot configure interface `{name}`: {err}")) })?; } @@ -512,7 +512,7 @@ async fn configure_link(configure: &Configure) -> Result<(), PluginError> { .execute() .await .map_err(|err| { - PluginError::Unavailable(format!( + OverlayError::Unavailable(format!( "cannot remove {cidr} from interface `{name}`: {err}" )) })?; @@ -526,7 +526,7 @@ async fn configure_link(configure: &Configure) -> Result<(), PluginError> { .execute() .await .map_err(|err| { - PluginError::Unavailable(format!( + OverlayError::Unavailable(format!( "cannot add {cidr} to interface `{name}`: {err}" )) })?; diff --git a/crates/tsunagi/src/dataplane/wireguard/provision/mock.rs b/crates/tsunagi/src/overlay/provision/mock.rs similarity index 97% rename from crates/tsunagi/src/dataplane/wireguard/provision/mock.rs rename to crates/tsunagi/src/overlay/provision/mock.rs index 2a4b720..16bcea4 100644 --- a/crates/tsunagi/src/dataplane/wireguard/provision/mock.rs +++ b/crates/tsunagi/src/overlay/provision/mock.rs @@ -9,7 +9,7 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex}; use crate::BoxFuture; -use crate::dataplane::PluginError; +use crate::overlay::OverlayError; use super::super::config::Cidr; use super::super::tun::{MemoryTun, MemoryTunFactory, TunFactory, TunRequest}; @@ -125,9 +125,9 @@ impl MockProvisioner { } } - fn apply(&self, plan: &InterfacePlan) -> Result { + fn apply(&self, plan: &InterfacePlan) -> Result { if let Some(reason) = &self.failure { - return Err(PluginError::Unavailable(reason.clone())); + return Err(OverlayError::Unavailable(reason.clone())); } let ours = self.owned().iter().any(|name| name == &plan.name); @@ -179,7 +179,7 @@ impl InterfaceProvisioner for MockProvisioner { fn reconcile<'a>( &'a self, plan: &'a InterfacePlan, - ) -> BoxFuture<'a, Result> { + ) -> BoxFuture<'a, Result> { Box::pin(async move { let changes = self.apply(plan)?; let device = if changes.create_link { @@ -195,7 +195,7 @@ impl InterfaceProvisioner for MockProvisioner { }) } - fn remove<'a>(&'a self, name: &'a str) -> BoxFuture<'a, Result<(), PluginError>> { + fn remove<'a>(&'a self, name: &'a str) -> BoxFuture<'a, Result<(), OverlayError>> { Box::pin(async move { self.host.lock().remove(name); self.owned().retain(|owned| owned != name); diff --git a/crates/tsunagi/src/dataplane/wireguard/provision/mod.rs b/crates/tsunagi/src/overlay/provision/mod.rs similarity index 98% rename from crates/tsunagi/src/dataplane/wireguard/provision/mod.rs rename to crates/tsunagi/src/overlay/provision/mod.rs index f5d93b9..5e8b83a 100644 --- a/crates/tsunagi/src/dataplane/wireguard/provision/mod.rs +++ b/crates/tsunagi/src/overlay/provision/mod.rs @@ -38,7 +38,7 @@ use std::collections::BTreeSet; use std::sync::Arc; use crate::BoxFuture; -use crate::dataplane::PluginError; +use crate::overlay::OverlayError; use super::config::Cidr; use super::tun::TunDevice; @@ -203,19 +203,19 @@ pub fn plan_changes( current: &InterfaceState, plan: &InterfacePlan, ours: bool, -) -> Result { +) -> Result { let mut changes = Changes::default(); match ¤t.kind { LinkKind::Foreign(kind) => { - return Err(PluginError::Unavailable(format!( + return Err(OverlayError::Unavailable(format!( "`{}` already exists and is a {kind} interface, not one of ours. \ Refusing to touch it. Run with a different interface prefix.", plan.name ))); } LinkKind::Tun if !ours && current.attached => { - return Err(PluginError::Unavailable(format!( + return Err(OverlayError::Unavailable(format!( "`{}` already exists and another process is attached to it. \ That is most likely a second agent on this host in the same \ network; give one of them a different interface prefix.", @@ -293,13 +293,13 @@ pub trait InterfaceProvisioner: Send + Sync + std::fmt::Debug + 'static { fn reconcile<'a>( &'a self, plan: &'a InterfacePlan, - ) -> BoxFuture<'a, Result>; + ) -> BoxFuture<'a, Result>; /// Removes an interface this provisioner created. /// /// Removing one that is already gone succeeds: this runs on the shutdown /// path, where the interface having vanished is the desired outcome. - fn remove<'a>(&'a self, name: &'a str) -> BoxFuture<'a, Result<(), PluginError>>; + fn remove<'a>(&'a self, name: &'a str) -> BoxFuture<'a, Result<(), OverlayError>>; } #[cfg(test)] diff --git a/crates/tsunagi/src/dataplane/wireguard/provision/privilege.rs b/crates/tsunagi/src/overlay/provision/privilege.rs similarity index 96% rename from crates/tsunagi/src/dataplane/wireguard/provision/privilege.rs rename to crates/tsunagi/src/overlay/provision/privilege.rs index 0e6fba6..033819f 100644 --- a/crates/tsunagi/src/dataplane/wireguard/provision/privilege.rs +++ b/crates/tsunagi/src/overlay/provision/privilege.rs @@ -78,7 +78,7 @@ mod linux_impl { use caps::{CapSet, Capability}; use super::Privilege; - use crate::dataplane::PluginError; + use crate::overlay::OverlayError; /// Whether this thread holds `CAP_NET_ADMIN` in its permitted set. pub fn probe_net_admin() -> Privilege { @@ -108,16 +108,16 @@ mod linux_impl { impl NetAdmin { /// Raises `CAP_NET_ADMIN` into the effective set. - pub fn acquire() -> Result { + pub fn acquire() -> Result { let already = caps::has_cap(None, CapSet::Effective, Capability::CAP_NET_ADMIN) .map_err(|err| { - PluginError::Unavailable(format!("cannot read capabilities: {err}")) + OverlayError::Unavailable(format!("cannot read capabilities: {err}")) })?; if already { return Ok(Self { raised: false }); } caps::raise(None, CapSet::Effective, Capability::CAP_NET_ADMIN).map_err(|err| { - PluginError::Unavailable(format!( + OverlayError::Unavailable(format!( "cannot raise CAP_NET_ADMIN: {err}. {}", Privilege::how_to_grant("tsunagi") )) diff --git a/crates/tsunagi/src/dataplane/wireguard/provision/unsupported.rs b/crates/tsunagi/src/overlay/provision/unsupported.rs similarity index 91% rename from crates/tsunagi/src/dataplane/wireguard/provision/unsupported.rs rename to crates/tsunagi/src/overlay/provision/unsupported.rs index 35b056a..3cd5d70 100644 --- a/crates/tsunagi/src/dataplane/wireguard/provision/unsupported.rs +++ b/crates/tsunagi/src/overlay/provision/unsupported.rs @@ -7,7 +7,7 @@ //! of provisioning and says what to do instead. use crate::BoxFuture; -use crate::dataplane::PluginError; +use crate::overlay::OverlayError; use super::{InterfacePlan, InterfaceProvisioner, Provisioned}; @@ -31,8 +31,8 @@ impl UnsupportedProvisioner { } } - fn refusal(&self) -> PluginError { - PluginError::Unavailable(format!( + fn refusal(&self) -> OverlayError { + OverlayError::Unavailable(format!( "managing the overlay interface is not implemented on {} yet. \ Run with `--no-tun` until it is: the tunnels still form, they just \ do not reach the operating system.", @@ -49,11 +49,11 @@ impl InterfaceProvisioner for UnsupportedProvisioner { fn reconcile<'a>( &'a self, _plan: &'a InterfacePlan, - ) -> BoxFuture<'a, Result> { + ) -> BoxFuture<'a, Result> { Box::pin(async move { Err(self.refusal()) }) } - fn remove<'a>(&'a self, _name: &'a str) -> BoxFuture<'a, Result<(), PluginError>> { + fn remove<'a>(&'a self, _name: &'a str) -> BoxFuture<'a, Result<(), OverlayError>> { // Nothing was ever created, so there is nothing to clean up and no // reason to fail a shutdown path. Box::pin(async move { Ok(()) }) diff --git a/crates/tsunagi/src/dataplane/wireguard/tun.rs b/crates/tsunagi/src/overlay/tun.rs similarity index 96% rename from crates/tsunagi/src/dataplane/wireguard/tun.rs rename to crates/tsunagi/src/overlay/tun.rs index f95f892..b172b34 100644 --- a/crates/tsunagi/src/dataplane/wireguard/tun.rs +++ b/crates/tsunagi/src/overlay/tun.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use bytes::Bytes; use crate::BoxFuture; -use crate::dataplane::PluginError; +use crate::overlay::OverlayError; /// What a device should look like once created. #[derive(Debug, Clone, PartialEq, Eq)] @@ -71,7 +71,7 @@ pub trait TunDevice: Send + Sync + std::fmt::Debug + 'static { fn recv(&self) -> BoxFuture<'_, Option>; /// Delivers a packet to the operating system. - fn send<'a>(&'a self, packet: Bytes) -> BoxFuture<'a, Result<(), PluginError>>; + fn send<'a>(&'a self, packet: Bytes) -> BoxFuture<'a, Result<(), OverlayError>>; } /// Creates packet interfaces. @@ -83,7 +83,7 @@ pub trait TunFactory: Send + Sync + std::fmt::Debug + 'static { fn create<'a>( &'a self, request: TunRequest, - ) -> BoxFuture<'a, Result, PluginError>>; + ) -> BoxFuture<'a, Result, OverlayError>>; /// Applies a changed request to an interface that already exists. /// @@ -94,7 +94,7 @@ pub trait TunFactory: Send + Sync + std::fmt::Debug + 'static { /// /// The default does nothing, which is right for a factory that only /// attaches to an interface somebody else prepared. - fn reconfigure<'a>(&'a self, _request: TunRequest) -> BoxFuture<'a, Result<(), PluginError>> { + fn reconfigure<'a>(&'a self, _request: TunRequest) -> BoxFuture<'a, Result<(), OverlayError>> { Box::pin(async move { Ok(()) }) } @@ -162,7 +162,7 @@ impl TunDevice for MemoryTun { Box::pin(async move { self.from_os_rx.lock().await.recv().await }) } - fn send<'a>(&'a self, packet: Bytes) -> BoxFuture<'a, Result<(), PluginError>> { + fn send<'a>(&'a self, packet: Bytes) -> BoxFuture<'a, Result<(), OverlayError>> { Box::pin(async move { let _ = self.to_os_tx.send(packet); Ok(()) @@ -223,7 +223,7 @@ impl TunFactory for MemoryTunFactory { fn create<'a>( &'a self, request: TunRequest, - ) -> BoxFuture<'a, Result, PluginError>> { + ) -> BoxFuture<'a, Result, OverlayError>> { Box::pin(async move { let device = MemoryTun::new(request.name, request.mtu); let mut guard = match self.created.lock() { @@ -248,7 +248,7 @@ mod system { use super::{TunDevice, TunRequest}; use crate::BoxFuture; - use crate::dataplane::PluginError; + use crate::overlay::OverlayError; /// A real TUN interface. /// @@ -303,14 +303,14 @@ mod system { }) } - fn send<'a>(&'a self, packet: Bytes) -> BoxFuture<'a, Result<(), PluginError>> { + fn send<'a>(&'a self, packet: Bytes) -> BoxFuture<'a, Result<(), OverlayError>> { Box::pin(async move { use tokio::io::AsyncWriteExt; let mut writer = self.writer.lock().await; writer .write_all(&packet) .await - .map_err(|err| PluginError::Other(format!("tun write failed: {err}"))) + .map_err(|err| OverlayError::Other(format!("tun write failed: {err}"))) }) } } @@ -320,7 +320,7 @@ mod system { /// Synchronous, and deliberately so: the caller holds a capability guard /// across this call, and such a guard must not span an `await` because /// Linux capabilities are per thread. - pub(crate) fn open_tun(request: &TunRequest) -> Result, PluginError> { + pub(crate) fn open_tun(request: &TunRequest) -> Result, OverlayError> { let mut config = tun::Configuration::default(); config.tun_name(&request.name); config.platform_config(|platform| { @@ -334,7 +334,7 @@ mod system { // information, so the flags match when attaching to one. let device = tun::create_as_async(&config).map_err(|err| { - PluginError::Unavailable(format!( + OverlayError::Unavailable(format!( "cannot create the TUN interface `{}`: {err}. Creating one needs \ CAP_NET_ADMIN; grant it with `setcap cap_net_admin+p`, or run with \ `--no-tun` to keep the tunnels off the operating system.",