diff options
| -rw-r--r-- | CHANGELOG.md | 4 | ||||
| -rw-r--r-- | talpid-core/src/split_tunnel/macos/mod.rs | 60 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/disconnected_state.rs | 10 |
3 files changed, 74 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 4695c1bc34..38c2bcc5b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,10 @@ Line wrap the file at 100 chars. Th - Never use OpenVPN as a fallback protocol when any of the following features is enabled: multihop, quantum-resistant tunnels, or DAITA. +#### macOS +- Disable split tunnel interface when disconnected. This prevents traffic from being sent through + the daemon when the VPN is disconnected. + ### Fixed - macOS and Linux: Fix potential crash when disconnecting with DAITA enabled. diff --git a/talpid-core/src/split_tunnel/macos/mod.rs b/talpid-core/src/split_tunnel/macos/mod.rs index 1c705f5e6d..b3acdb0136 100644 --- a/talpid-core/src/split_tunnel/macos/mod.rs +++ b/talpid-core/src/split_tunnel/macos/mod.rs @@ -113,6 +113,11 @@ enum Message { result_tx: oneshot::Sender<Result<(), Error>>, vpn_interface: VpnInterface, }, + /// Remove VPN tunnel interface. It is sufficient to call this when entering the disconnected + /// state, to avoid pointless cleanup during reconnects. + ResetTunnel { + result_tx: oneshot::Sender<Result<(), Error>>, + }, } /// Handle for interacting with the split tunnel module @@ -157,6 +162,14 @@ impl Handle { }); result_rx.await.map_err(|_| Error::unavailable())? } + + /// Forget the VPN tunnel interface. This destroys the split tunneling interface when it is + /// active. + pub async fn reset_tunnel(&self) -> Result<(), Error> { + let (result_tx, result_rx) = oneshot::channel(); + let _ = self.tx.send(Message::ResetTunnel { result_tx }); + result_rx.await.map_err(|_| Error::unavailable())? + } } impl SplitTunnel { @@ -259,6 +272,9 @@ impl SplitTunnel { } => { let _ = result_tx.send(self.state.set_tunnel(vpn_interface).await); } + Message::ResetTunnel { result_tx } => { + let _ = result_tx.send(self.state.reset_tunnel().await); + } } true } @@ -473,6 +489,50 @@ impl State { } } + /// Forget the VPN tunnel interface. This destroys the split tunneling interface when it is + /// active. + pub async fn reset_tunnel(&mut self) -> Result<(), Error> { + self.transition(|state| state.reset_tunnel_inner()).await + } + + async fn reset_tunnel_inner(self) -> Result<Self, ErrorWithTransition> { + match self { + // If split tunneling is currently active, that means that there are paths to exclude, + // so shut down the ST utun but keep the process monitor. + State::Active { + route_manager, + process, + tun_handle, + vpn_interface: _, + } => { + if let Err(error) = tun_handle.shutdown().await { + log::error!("Failed to stop split tunnel: {error}"); + } + Ok(State::ProcessMonitorOnly { + route_manager, + process, + }) + } + // If we're in standby mode, simply forget the VPN interface. + State::StandBy { + route_manager, + vpn_interface: _, + } => Ok(State::NoExclusions { route_manager }), + // If we're in `Failed`, just forget the VPN interface. + State::Failed { + route_manager, + vpn_interface: _, + cause, + } => Ok(State::Failed { + route_manager, + vpn_interface: None, + cause, + }), + // For any other state, do nothing. + _ => Ok(self), + } + } + /// Update VPN tunnel interface that non-excluded packets are sent on async fn set_tunnel(&mut self, vpn_interface: VpnInterface) -> Result<(), Error> { self.transition(move |self_| self_.set_tunnel_inner(vpn_interface)) diff --git a/talpid-core/src/tunnel_state_machine/disconnected_state.rs b/talpid-core/src/tunnel_state_machine/disconnected_state.rs index 4ee19a5e16..74cd3b65c4 100644 --- a/talpid-core/src/tunnel_state_machine/disconnected_state.rs +++ b/talpid-core/src/tunnel_state_machine/disconnected_state.rs @@ -21,6 +21,16 @@ impl DisconnectedState { should_reset_firewall: bool, ) -> (Box<dyn TunnelState>, TunnelStateTransition) { #[cfg(target_os = "macos")] + if let Err(err) = shared_values + .runtime + .block_on(shared_values.split_tunnel.reset_tunnel()) + { + log::error!( + "{}", + err.display_chain_with_msg("Failed to disable split tunneling") + ); + } + #[cfg(target_os = "macos")] if shared_values.block_when_disconnected { if let Err(err) = Self::setup_local_dns_config(shared_values) { log::error!( |
