Allow no-tunnel passthrough mode
This commit is contained in:
parent
450e9c6fcd
commit
7ade60646b
3 changed files with 76 additions and 38 deletions
|
|
@ -71,21 +71,20 @@ impl DaemonRPCServer {
|
|||
self.network_update_chan.0.send(()).map_err(proc_err)
|
||||
}
|
||||
|
||||
async fn resolve_tunnel(&self) -> Result<Option<ResolvedTunnel>, RspStatus> {
|
||||
async fn resolve_tunnel(&self) -> Result<ResolvedTunnel, RspStatus> {
|
||||
let conn = self.get_connection()?;
|
||||
let networks = list_networks(&conn).map_err(proc_err)?;
|
||||
ResolvedTunnel::from_networks(&networks).map_err(proc_err)
|
||||
}
|
||||
|
||||
async fn current_tunnel_configuration(&self) -> Result<TunnelConfigurationResponse, RspStatus> {
|
||||
match self.resolve_tunnel().await? {
|
||||
Some(config) => {
|
||||
let config = config.server_config().map_err(proc_err)?;
|
||||
let config = self
|
||||
.resolve_tunnel()
|
||||
.await?
|
||||
.server_config()
|
||||
.map_err(proc_err)?;
|
||||
Ok(configuration_rsp(config))
|
||||
}
|
||||
None => Ok(empty_configuration_rsp()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn stop_active_tunnel(&self) -> Result<bool, RspStatus> {
|
||||
let current = { self.active_tunnel.write().await.take() };
|
||||
|
|
@ -114,10 +113,6 @@ impl DaemonRPCServer {
|
|||
|
||||
async fn reconcile_runtime(&self) -> Result<(), RspStatus> {
|
||||
let desired = self.resolve_tunnel().await?;
|
||||
let Some(desired) = desired else {
|
||||
let _ = self.stop_active_tunnel().await?;
|
||||
return Ok(());
|
||||
};
|
||||
let needs_restart = {
|
||||
let guard = self.active_tunnel.read().await;
|
||||
guard
|
||||
|
|
@ -163,10 +158,7 @@ impl Tunnel for DaemonRPCServer {
|
|||
}
|
||||
|
||||
async fn tunnel_start(&self, _request: Request<Empty>) -> Result<Response<Empty>, RspStatus> {
|
||||
let desired = self
|
||||
.resolve_tunnel()
|
||||
.await?
|
||||
.ok_or_else(|| RspStatus::failed_precondition("no stored network configured"))?;
|
||||
let desired = self.resolve_tunnel().await?;
|
||||
let already_running = {
|
||||
let guard = self.active_tunnel.read().await;
|
||||
guard
|
||||
|
|
@ -285,13 +277,6 @@ fn configuration_rsp(config: ServerConfig) -> TunnelConfigurationResponse {
|
|||
}
|
||||
}
|
||||
|
||||
fn empty_configuration_rsp() -> TunnelConfigurationResponse {
|
||||
TunnelConfigurationResponse {
|
||||
mtu: 1500,
|
||||
addresses: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn status_rsp(state: RunState) -> TunnelStatusResponse {
|
||||
TunnelStatusResponse {
|
||||
state: state.to_rpc().into(),
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ mod tests {
|
|||
client::BurrowClient,
|
||||
grpc_defs::{
|
||||
Empty, Network, NetworkListResponse, NetworkReorderRequest, NetworkType,
|
||||
TunnelConfigurationResponse,
|
||||
TunnelConfigurationResponse, TunnelStatusResponse,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -111,6 +111,11 @@ mod tests {
|
|||
.network_list(Empty {})
|
||||
.await?
|
||||
.into_inner();
|
||||
let mut status_stream = client
|
||||
.tunnel_client
|
||||
.tunnel_status(Empty {})
|
||||
.await?
|
||||
.into_inner();
|
||||
|
||||
let initial_config = next_configuration(&mut config_stream).await?;
|
||||
assert!(initial_config.addresses.is_empty());
|
||||
|
|
@ -119,12 +124,27 @@ mod tests {
|
|||
let initial_networks = next_networks(&mut network_stream).await?;
|
||||
assert!(initial_networks.network.is_empty());
|
||||
|
||||
let start_err = client
|
||||
.tunnel_client
|
||||
.tunnel_start(Empty {})
|
||||
.await
|
||||
.expect_err("starting without a stored network should fail");
|
||||
assert_eq!(start_err.code(), tonic::Code::FailedPrecondition);
|
||||
let initial_status = next_status(&mut status_stream).await?;
|
||||
assert_eq!(
|
||||
initial_status.state(),
|
||||
crate::daemon::rpc::grpc_defs::State::Stopped
|
||||
);
|
||||
|
||||
client.tunnel_client.tunnel_start(Empty {}).await?;
|
||||
|
||||
let passthrough_status = next_status(&mut status_stream).await?;
|
||||
assert_eq!(
|
||||
passthrough_status.state(),
|
||||
crate::daemon::rpc::grpc_defs::State::Running
|
||||
);
|
||||
|
||||
client.tunnel_client.tunnel_stop(Empty {}).await?;
|
||||
|
||||
let stopped_status = next_status(&mut status_stream).await?;
|
||||
assert_eq!(
|
||||
stopped_status.state(),
|
||||
crate::daemon::rpc::grpc_defs::State::Stopped
|
||||
);
|
||||
|
||||
client
|
||||
.networks_client
|
||||
|
|
@ -246,6 +266,14 @@ Endpoint = wg.burrow.rs:51820
|
|||
.ok_or_else(|| anyhow!("network stream ended unexpectedly"))
|
||||
}
|
||||
|
||||
async fn next_status(
|
||||
stream: &mut tonic::Streaming<TunnelStatusResponse>,
|
||||
) -> Result<TunnelStatusResponse> {
|
||||
timeout(Duration::from_secs(5), stream.message())
|
||||
.await??
|
||||
.ok_or_else(|| anyhow!("status stream ended unexpectedly"))
|
||||
}
|
||||
|
||||
fn network_ids(response: &NetworkListResponse) -> Vec<(i32, NetworkType)> {
|
||||
response
|
||||
.network
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use crate::{
|
|||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum RuntimeIdentity {
|
||||
Passthrough,
|
||||
Network {
|
||||
id: i32,
|
||||
network_type: NetworkType,
|
||||
|
|
@ -24,6 +25,9 @@ pub enum RuntimeIdentity {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ResolvedTunnel {
|
||||
Passthrough {
|
||||
identity: RuntimeIdentity,
|
||||
},
|
||||
WireGuard {
|
||||
identity: RuntimeIdentity,
|
||||
config: Config,
|
||||
|
|
@ -35,9 +39,11 @@ pub enum ResolvedTunnel {
|
|||
}
|
||||
|
||||
impl ResolvedTunnel {
|
||||
pub fn from_networks(networks: &[Network]) -> Result<Option<Self>> {
|
||||
pub fn from_networks(networks: &[Network]) -> Result<Self> {
|
||||
let Some(network) = networks.first() else {
|
||||
return Ok(None);
|
||||
return Ok(Self::Passthrough {
|
||||
identity: RuntimeIdentity::Passthrough,
|
||||
});
|
||||
};
|
||||
|
||||
let identity = RuntimeIdentity::Network {
|
||||
|
|
@ -51,23 +57,30 @@ impl ResolvedTunnel {
|
|||
let payload = String::from_utf8(network.payload.clone())
|
||||
.context("wireguard payload must be valid UTF-8")?;
|
||||
let config = Config::from_content_fmt(&payload, "ini")?;
|
||||
Ok(Some(Self::WireGuard { identity, config }))
|
||||
Ok(Self::WireGuard { identity, config })
|
||||
}
|
||||
NetworkType::HackClub => {
|
||||
let config = HackClubNetworkConfig::from_payload(&network.payload)?;
|
||||
Ok(Some(Self::HackClub { identity, config }))
|
||||
Ok(Self::HackClub { identity, config })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn identity(&self) -> &RuntimeIdentity {
|
||||
match self {
|
||||
Self::WireGuard { identity, .. } | Self::HackClub { identity, .. } => identity,
|
||||
Self::Passthrough { identity }
|
||||
| Self::WireGuard { identity, .. }
|
||||
| Self::HackClub { identity, .. } => identity,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server_config(&self) -> Result<ServerConfig> {
|
||||
match self {
|
||||
Self::Passthrough { .. } => Ok(ServerConfig {
|
||||
address: Vec::new(),
|
||||
name: None,
|
||||
mtu: Some(1500),
|
||||
}),
|
||||
Self::WireGuard { config, .. } => ServerConfig::try_from(config),
|
||||
Self::HackClub { config, .. } => Ok(ServerConfig {
|
||||
address: config.local_addresses.clone(),
|
||||
|
|
@ -82,6 +95,7 @@ impl ResolvedTunnel {
|
|||
tun_interface: Arc<RwLock<Option<TunInterface>>>,
|
||||
) -> Result<ActiveTunnel> {
|
||||
match self {
|
||||
Self::Passthrough { identity } => Ok(ActiveTunnel::Passthrough { identity }),
|
||||
Self::WireGuard { identity, config } => {
|
||||
let tun = TunOptions::new().open()?;
|
||||
tun_interface.write().await.replace(tun);
|
||||
|
|
@ -118,6 +132,9 @@ impl ResolvedTunnel {
|
|||
}
|
||||
|
||||
pub enum ActiveTunnel {
|
||||
Passthrough {
|
||||
identity: RuntimeIdentity,
|
||||
},
|
||||
WireGuard {
|
||||
identity: RuntimeIdentity,
|
||||
interface: Arc<RwLock<WireGuardInterface>>,
|
||||
|
|
@ -132,12 +149,15 @@ pub enum ActiveTunnel {
|
|||
impl ActiveTunnel {
|
||||
pub fn identity(&self) -> &RuntimeIdentity {
|
||||
match self {
|
||||
Self::WireGuard { identity, .. } | Self::HackClub { identity, .. } => identity,
|
||||
Self::Passthrough { identity }
|
||||
| Self::WireGuard { identity, .. }
|
||||
| Self::HackClub { identity, .. } => identity,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn shutdown(self, tun_interface: &Arc<RwLock<Option<TunInterface>>>) -> Result<()> {
|
||||
match self {
|
||||
Self::Passthrough { .. } => Ok(()),
|
||||
Self::WireGuard { interface, task, .. } => {
|
||||
interface.read().await.remove_tun().await;
|
||||
let task_result = task.await;
|
||||
|
|
@ -174,7 +194,12 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn no_networks_resolves_to_no_tunnel() {
|
||||
assert!(ResolvedTunnel::from_networks(&[]).unwrap().is_none());
|
||||
fn no_networks_resolve_to_passthrough() {
|
||||
let resolved = ResolvedTunnel::from_networks(&[]).unwrap();
|
||||
assert_eq!(resolved.identity(), &RuntimeIdentity::Passthrough);
|
||||
assert_eq!(
|
||||
resolved.server_config().unwrap().address,
|
||||
Vec::<String>::new()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue