diff options
| author | David Lönnhager <david.l@mullvad.net> | 2025-07-25 14:17:06 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2025-07-25 15:43:29 +0200 |
| commit | e2d91ff3d404d17a66c925bc5cd22dc29417550b (patch) | |
| tree | 55e3de5d39638022dd89c3083ea25b39edf08f64 | |
| parent | 427ebeed0f7004357ddc3dcd2c72330b0373ca9b (diff) | |
| download | mullvadvpn-e2d91ff3d404d17a66c925bc5cd22dc29417550b.tar.xz mullvadvpn-e2d91ff3d404d17a66c925bc5cd22dc29417550b.zip | |
Create new boringtun devices when toggling multihop
| -rw-r--r-- | Cargo.lock | 2 | ||||
| -rw-r--r-- | talpid-tunnel/src/tun_provider/android/mod.rs | 3 | ||||
| -rw-r--r-- | talpid-wireguard/Cargo.toml | 2 | ||||
| -rw-r--r-- | talpid-wireguard/src/boringtun/mod.rs | 162 |
4 files changed, 103 insertions, 66 deletions
diff --git a/Cargo.lock b/Cargo.lock index 74c8ccd761..f6c6c26dfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -444,7 +444,7 @@ dependencies = [ [[package]] name = "boringtun" version = "0.6.0" -source = "git+https://github.com/mullvad/boringtun?rev=9194fbfd5ba578408887429c59c3b9d9171722fa#9194fbfd5ba578408887429c59c3b9d9171722fa" +source = "git+https://github.com/mullvad/boringtun?rev=ad10b7b1aecc8bbd73925bf6c332f97f27923c77#ad10b7b1aecc8bbd73925bf6c332f97f27923c77" dependencies = [ "aead", "async-trait", diff --git a/talpid-tunnel/src/tun_provider/android/mod.rs b/talpid-tunnel/src/tun_provider/android/mod.rs index 0ba8b340f2..9406c3f936 100644 --- a/talpid-tunnel/src/tun_provider/android/mod.rs +++ b/talpid-tunnel/src/tun_provider/android/mod.rs @@ -401,7 +401,8 @@ impl VpnServiceTun { } /// Allow a socket to bypass the tunnel. - pub fn bypass(&mut self, socket: &impl AsFd) -> Result<(), Error> { + // TODO: is it ok to not mutably borrow self? + pub fn bypass(&self, socket: &impl AsFd) -> Result<(), Error> { let env = JnixEnv::from( self.jvm .attach_current_thread_as_daemon() diff --git a/talpid-wireguard/Cargo.toml b/talpid-wireguard/Cargo.toml index 87372c49b3..a4f12408b6 100644 --- a/talpid-wireguard/Cargo.toml +++ b/talpid-wireguard/Cargo.toml @@ -46,7 +46,7 @@ tokio-stream = { version = "0.1", features = ["io-util"] } optional = true features = ["device", "tun"] git = "https://github.com/mullvad/boringtun" -rev = "9194fbfd5ba578408887429c59c3b9d9171722fa" +rev = "ad10b7b1aecc8bbd73925bf6c332f97f27923c77" [target.'cfg(unix)'.dependencies] nix = { workspace = true, features = ["fs"] } diff --git a/talpid-wireguard/src/boringtun/mod.rs b/talpid-wireguard/src/boringtun/mod.rs index 93da4b141e..89b0272956 100644 --- a/talpid-wireguard/src/boringtun/mod.rs +++ b/talpid-wireguard/src/boringtun/mod.rs @@ -41,7 +41,13 @@ const PACKET_CHANNEL_CAPACITY: usize = 100; pub struct BoringTun { /// Device handles - devices: Devices, + // TODO: Can we not store this in an option? + devices: Option<Devices>, + + tun: Arc<AsyncDevice>, + + #[cfg(target_os = "android")] + android_tun: Arc<Tun>, /// Tunnel config config: Config, @@ -67,7 +73,7 @@ enum Devices { #[cfg(target_os = "android")] struct AndroidUdpSocketFactory { - pub tun: Tun, + pub tun: Arc<Tun>, } #[cfg(target_os = "android")] @@ -111,11 +117,6 @@ pub async fn open_boringtun_tunnel( } }; - let (entry_api, entry_api_server) = ApiServer::new(); - let boringtun_entry_config = DeviceConfig { - api: Some(entry_api_server), - }; - #[cfg(target_os = "android")] let (tun, async_tun) = { let _ = routes; // TODO: do we need this? @@ -142,7 +143,7 @@ pub async fn open_boringtun_tunnel( let device = tun07::Device::new(&tun_config).unwrap(); - (tun, tun07::AsyncDevice::new(device).unwrap()) + (Arc::new(tun), tun07::AsyncDevice::new(device).unwrap()) }; let interface_name = async_tun.deref().tun_name().unwrap(); @@ -150,7 +151,52 @@ pub async fn open_boringtun_tunnel( log::info!("passing tunnel dev to boringtun"); let async_tun = Arc::new(async_tun); - let mut boringtun = if config.exit_peer.is_some() { + let mut boringtun = BoringTun { + config: config.clone(), + interface_name, + tun: async_tun.clone(), + #[cfg(target_os = "android")] + android_tun: tun.clone(), + devices: Some( + create_devices( + config, + async_tun, + #[cfg(target_os = "android")] + tun, + ) + .await, + ), + }; + + // FIXME: double clone + boringtun.set_config(config.clone()).await?; + + log::info!( + "This tunnel was brought to you by... + ......................................................... + ..*...*.. .--. .---. ..*....*. + ...*..... | ) o | ......*.. + .*..*..*. |--: .-. .--.. .--. .-..|. . .--. ...*..... + ...*..... | )( )| | | |( ||| | | | .*.....*. + *.....*.. '--' `-' ' -' `-' `-`-`|'`--`-' `- .....*... + ......... ._.' ..*...*.. + ..*...*.............................................*...." + ); + + Ok(boringtun) +} + +async fn create_devices( + config: &Config, + async_tun: Arc<AsyncDevice>, + #[cfg(target_os = "android")] tun: Arc<Tun>, +) -> Devices { + let (entry_api, entry_api_server) = ApiServer::new(); + let boringtun_entry_config = DeviceConfig { + api: Some(entry_api_server), + }; + + if config.exit_peer.is_some() { // multihop let source_v4 = config.tunnel.addresses.iter().find_map(|ip| match ip { @@ -178,8 +224,7 @@ pub async fn open_boringtun_tunnel( api: Some(exit_api_server), }, ) - .await - .map_err(TunnelError::BoringTunDevice)?; + .await; #[cfg(target_os = "android")] let factory = AndroidUdpSocketFactory { tun }; @@ -188,19 +233,13 @@ pub async fn open_boringtun_tunnel( let factory = UdpSocketFactory; let entry_device = - EntryDevice::new(factory, channel.clone(), channel, boringtun_entry_config) - .await - .map_err(TunnelError::BoringTunDevice)?; + EntryDevice::new(factory, channel.clone(), channel, boringtun_entry_config).await; - BoringTun { - config: config.clone(), - interface_name, - devices: Devices::Multihop { - entry_device, - entry_api, - exit_device, - exit_api, - }, + Devices::Multihop { + entry_device, + entry_api, + exit_device, + exit_api, } } else { #[cfg(target_os = "android")] @@ -215,35 +254,13 @@ pub async fn open_boringtun_tunnel( async_tun, boringtun_entry_config, ) - .await - .map_err(TunnelError::BoringTunDevice)?; + .await; - BoringTun { - devices: Devices::Singlehop { - device, - api: entry_api, - }, - config: config.clone(), - interface_name, + Devices::Singlehop { + device, + api: entry_api, } - }; - - // FIXME: double clone - boringtun.set_config(config.clone()).await?; - - log::info!( - "This tunnel was brought to you by... - ......................................................... - ..*...*.. .--. .---. ..*....*. - ...*..... | ) o | ......*.. - .*..*..*. |--: .-. .--.. .--. .-..|. . .--. ...*..... - ...*..... | )( )| | | |( ||| | | | .*.....*. - *.....*.. '--' `-' ' -' `-' `-`-`|'`--`-' `- .....*... - ......... ._.' ..*...*.. - ..*...*.............................................*...." - ); - - Ok(boringtun) + } } #[async_trait::async_trait] @@ -252,10 +269,10 @@ impl Tunnel for BoringTun { self.interface_name.clone() } - fn stop(self: Box<Self>) -> Result<(), TunnelError> { + fn stop(mut self: Box<Self>) -> Result<(), TunnelError> { log::info!("BoringTun::stop"); // remove me tokio::runtime::Handle::current().block_on(async { - match self.devices { + match self.devices.take().unwrap() { Devices::Singlehop { device, .. } => { device.stop().await; } @@ -276,7 +293,7 @@ impl Tunnel for BoringTun { async fn get_tunnel_stats(&self) -> Result<StatsMap, TunnelError> { let mut stats = StatsMap::default(); - let apis = match &self.devices { + let apis = match self.devices.as_ref().unwrap() { Devices::Singlehop { api, .. } => [Some(api), None], Devices::Multihop { entry_api, @@ -311,13 +328,36 @@ impl Tunnel for BoringTun { config: Config, ) -> std::pin::Pin<Box<dyn Future<Output = Result<(), TunnelError>> + Send + 'a>> { Box::pin(async move { - self.config = config; - match &mut self.devices { + let old_config = std::mem::replace(&mut self.config, config); + + if old_config.is_multihop() != self.config.is_multihop() { + // TODO: Update existing tunnels? + match self.devices.take().unwrap() { + Devices::Singlehop { device, .. } => { + device.stop().await; + } + Devices::Multihop { + entry_device, + exit_device, + .. + } => { + exit_device.stop().await; + entry_device.stop().await; + } + } + + self.devices = Some( + create_devices( + &self.config, + self.tun.clone(), + #[cfg(target_os = "android")] + self.android_tun.clone(), + ) + .await, + ); + } + match self.devices.as_mut().unwrap() { Devices::Singlehop { api, .. } => { - assert!( - self.config.exit_peer.is_none(), - "todo: support switching between single and multihop" - ); set_boringtun_config(api, &self.config).await?; } Devices::Multihop { @@ -325,10 +365,6 @@ impl Tunnel for BoringTun { exit_api, .. } => { - assert!( - self.config.exit_peer.is_some(), - "todo: support switching between single and multihop" - ); set_boringtun_entry_config(entry_api, &self.config).await?; set_boringtun_exit_config(exit_api, &self.config).await?; } |
