diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-07-16 02:56:14 +0000 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-07-20 13:10:19 +0000 |
| commit | 1774c36b7d53d88050dd068cbf3fcf733d78e2d2 (patch) | |
| tree | 5bafc4263a9218576af8b2d7861a15466979a83e | |
| parent | 9c1d25cbcba7310bc2bdb5c527a74ee01c812d42 (diff) | |
| download | mullvadvpn-1774c36b7d53d88050dd068cbf3fcf733d78e2d2.tar.xz mullvadvpn-1774c36b7d53d88050dd068cbf3fcf733d78e2d2.zip | |
Handle tun device reuse in the Java side
| -rw-r--r-- | android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt | 59 | ||||
| -rw-r--r-- | mullvad-jni/src/talpid_vpn_service.rs | 23 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/tun_provider/android/mod.rs | 159 |
3 files changed, 179 insertions, 62 deletions
diff --git a/android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt b/android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt index 24bcd16b30..8f095641a1 100644 --- a/android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt +++ b/android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt @@ -1,12 +1,22 @@ package net.mullvad.talpid import android.net.VpnService +import android.os.ParcelFileDescriptor import java.net.Inet4Address import java.net.Inet6Address import java.net.InetAddress +import kotlin.properties.Delegates.observable import net.mullvad.talpid.tun_provider.TunConfig open class TalpidVpnService : VpnService() { + private var activeTunDevice by observable<Int?>(null) { _, oldTunDevice, _ -> + oldTunDevice?.let { oldTunFd -> + ParcelFileDescriptor.adoptFd(oldTunFd).close() + } + } + + private var currentTunConfig = defaultTunConfig() + val connectivityListener = ConnectivityListener() override fun onCreate() { @@ -17,7 +27,53 @@ open class TalpidVpnService : VpnService() { connectivityListener.unregister(this) } - fun createTun(config: TunConfig): Int { + fun getTun(config: TunConfig): Int { + synchronized(this) { + val tunDevice = activeTunDevice + + if (config == currentTunConfig && tunDevice != null) { + return tunDevice + } else { + val newTunDevice = createTun(config) + + currentTunConfig = config + activeTunDevice = newTunDevice + + return newTunDevice + } + } + } + + fun createTun() { + synchronized(this) { + activeTunDevice = createTun(currentTunConfig) + } + } + + fun createTunIfClosed() { + synchronized(this) { + if (activeTunDevice == null) { + activeTunDevice = createTun(currentTunConfig) + } + } + } + + fun recreateTunIfOpen(config: TunConfig) { + synchronized(this) { + if (activeTunDevice != null) { + currentTunConfig = config + activeTunDevice = createTun(config) + } + } + } + + fun closeTun() { + synchronized(this) { + activeTunDevice = null + } + } + + private fun createTun(config: TunConfig): Int { if (VpnService.prepare(this) != null) { // VPN permission wasn't granted return -1 @@ -63,5 +119,6 @@ open class TalpidVpnService : VpnService() { } } + private external fun defaultTunConfig(): TunConfig private external fun waitForTunnelUp(tunFd: Int, isIpv6Enabled: Boolean) } diff --git a/mullvad-jni/src/talpid_vpn_service.rs b/mullvad-jni/src/talpid_vpn_service.rs index 0e266970ba..e315b55e15 100644 --- a/mullvad-jni/src/talpid_vpn_service.rs +++ b/mullvad-jni/src/talpid_vpn_service.rs @@ -1,8 +1,11 @@ use ipnetwork::IpNetwork; -use jnix::jni::{ - objects::JObject, - sys::{jboolean, jint, JNI_FALSE}, - JNIEnv, +use jnix::{ + jni::{ + objects::JObject, + sys::{jboolean, jint, JNI_FALSE}, + JNIEnv, + }, + IntoJava, JnixEnv, }; use nix::sys::{ select::{pselect, FdSet}, @@ -15,6 +18,7 @@ use std::{ os::unix::io::RawFd, time::{Duration, Instant}, }; +use talpid_core::tunnel::TunConfig; use talpid_types::ErrorExt; #[derive(Debug, err_derive::Error)] @@ -31,6 +35,17 @@ enum Error { #[no_mangle] #[allow(non_snake_case)] +pub extern "system" fn Java_net_mullvad_talpid_TalpidVpnService_defaultTunConfig<'env>( + env: JNIEnv<'env>, + _this: JObject<'_>, +) -> JObject<'env> { + let env = JnixEnv::from(env); + + TunConfig::default().into_java(&env).forget() +} + +#[no_mangle] +#[allow(non_snake_case)] pub extern "system" fn Java_net_mullvad_talpid_TalpidVpnService_waitForTunnelUp( _: JNIEnv<'_>, _this: JObject<'_>, diff --git a/talpid-core/src/tunnel/tun_provider/android/mod.rs b/talpid-core/src/tunnel/tun_provider/android/mod.rs index 3cf6f16cd5..e4e8f4fbac 100644 --- a/talpid-core/src/tunnel/tun_provider/android/mod.rs +++ b/talpid-core/src/tunnel/tun_provider/android/mod.rs @@ -12,12 +12,11 @@ use jnix::{ IntoJava, JnixEnv, }; use std::{ - fs::File, net::{IpAddr, Ipv4Addr, Ipv6Addr}, - os::unix::io::{AsRawFd, FromRawFd, RawFd}, + os::unix::io::{AsRawFd, RawFd}, sync::Arc, }; -use talpid_types::android::AndroidContext; +use talpid_types::{android::AndroidContext, ErrorExt}; /// Errors that occur while setting up VpnService tunnel. @@ -58,7 +57,6 @@ pub struct AndroidTunProvider { jvm: Arc<JavaVM>, class: GlobalRef, object: GlobalRef, - active_tun: Option<File>, last_tun_config: TunConfig, allow_lan: bool, } @@ -78,7 +76,6 @@ impl AndroidTunProvider { jvm: context.jvm, class: talpid_vpn_service_class, object: context.vpn_service, - active_tun: None, last_tun_config: TunConfig::default(), allow_lan, } @@ -87,10 +84,7 @@ impl AndroidTunProvider { pub fn set_allow_lan(&mut self, allow_lan: bool) -> Result<(), Error> { if self.allow_lan != allow_lan { self.allow_lan = allow_lan; - - if self.active_tun.is_some() { - self.create_tun()?; - } + self.recreate_tun_if_open()?; } Ok(()) @@ -98,7 +92,9 @@ impl AndroidTunProvider { /// Retrieve a tunnel device with the provided configuration. pub fn get_tun(&mut self, config: TunConfig) -> Result<VpnServiceTun, Error> { - let tun_fd = self.get_tun_fd(config)?; + let tun_fd = self.get_tun_fd(config.clone())?; + + self.last_tun_config = config; let jvm = unsafe { JavaVM::from_raw(self.jvm.get_java_vm_pointer()) } .map_err(Error::CloneJavaVm)?; @@ -116,34 +112,98 @@ impl AndroidTunProvider { /// Will open a new tunnel if there is already an active tunnel. The previous tunnel will be /// closed. pub fn create_tun(&mut self) -> Result<(), Error> { - self.open_tun(self.last_tun_config.clone()) + let result = self.call_method( + "createTun", + "()V", + JavaType::Primitive(Primitive::Void), + &[], + )?; + + match result { + JValue::Void => Ok(()), + value => Err(Error::InvalidMethodResult( + "createTun", + format!("{:?}", value), + )), + } } /// Open a tunnel device using the previous or the default configuration if there is no /// currently active tunnel. pub fn create_tun_if_closed(&mut self) -> Result<(), Error> { - if self.active_tun.is_none() { - self.create_tun()?; - } + let result = self.call_method( + "createTunIfClosed", + "()V", + JavaType::Primitive(Primitive::Void), + &[], + )?; - Ok(()) + match result { + JValue::Void => Ok(()), + value => Err(Error::InvalidMethodResult( + "createTunIfClosed", + format!("{:?}", value), + )), + } } /// Close currently active tunnel device. pub fn close_tun(&mut self) { - self.active_tun = None; + let result = self.call_method("closeTun", "()V", JavaType::Primitive(Primitive::Void), &[]); + + let error = match result { + Ok(JValue::Void) => None, + Ok(value) => Some(Error::InvalidMethodResult( + "closeTun", + format!("{:?}", value), + )), + Err(error) => Some(error), + }; + + if let Some(error) = error { + log::error!( + "{}", + error.display_chain_with_msg("Failed to close the tunnel") + ); + } } fn get_tun_fd(&mut self, config: TunConfig) -> Result<RawFd, Error> { - if self.active_tun.is_none() || self.last_tun_config != config { - self.open_tun(config)?; + let env = self.env()?; + let actual_config = self.prepare_tun_config(config); + let java_config = actual_config.into_java(&env); + + let result = self.call_method( + "getTun", + "(Lnet/mullvad/talpid/tun_provider/TunConfig;)I", + JavaType::Primitive(Primitive::Int), + &[JValue::Object(java_config.as_obj())], + )?; + + match result { + JValue::Int(0) => Err(Error::TunnelDeviceError), + JValue::Int(-1) => Err(Error::PermissionDenied), + JValue::Int(fd) => Ok(fd), + value => Err(Error::InvalidMethodResult("getTun", format!("{:?}", value))), } + } + + fn recreate_tun_if_open(&mut self) -> Result<(), Error> { + let env = self.env()?; + let actual_config = self.prepare_tun_config(self.last_tun_config.clone()); + let java_config = actual_config.into_java(&env); + + let result = self.call_method( + "recreateTunIfOpen", + "(Lnet/mullvad/talpid/tun_provider/TunConfig;)V", + JavaType::Primitive(Primitive::Void), + &[JValue::Object(java_config.as_obj())], + )?; - Ok(self - .active_tun - .as_ref() - .expect("Tunnel should be configured") - .as_raw_fd()) + match result { + JValue::Void => Ok(()), + value => Err(Error::InvalidMethodResult("getTun", format!("{:?}", value))), + } } fn prepare_tun_config(&self, config: TunConfig) -> TunConfig { @@ -189,48 +249,33 @@ impl AndroidTunProvider { } } - fn open_tun(&mut self, config: TunConfig) -> Result<(), Error> { - let actual_config = self.prepare_tun_config(config.clone()); - + fn call_method( + &self, + name: &'static str, + signature: &str, + return_type: JavaType, + parameters: &[JValue<'_>], + ) -> Result<JValue<'_>, Error> { let env = JnixEnv::from( self.jvm .attach_current_thread_as_daemon() .map_err(Error::AttachJvmToThread)?, ); - let create_tun_method = env - .get_method_id( - &self.class, - "createTun", - "(Lnet/mullvad/talpid/tun_provider/TunConfig;)I", - ) - .map_err(|cause| Error::FindMethod("createTun", cause))?; + let method_id = env + .get_method_id(&self.class, name, signature) + .map_err(|cause| Error::FindMethod(name, cause))?; - let java_config = actual_config.clone().into_java(&env); - let result = env - .call_method_unchecked( - self.object.as_obj(), - create_tun_method, - JavaType::Primitive(Primitive::Int), - &[JValue::Object(java_config.as_obj())], - ) - .map_err(|cause| Error::CallMethod("createTun", cause))?; - - match result { - JValue::Int(0) => Err(Error::TunnelDeviceError), - JValue::Int(-1) => Err(Error::PermissionDenied), - JValue::Int(fd) => { - let tun = unsafe { File::from_raw_fd(fd) }; + env.call_method_unchecked(self.object.as_obj(), method_id, return_type, parameters) + .map_err(|cause| Error::CallMethod(name, cause)) + } - self.active_tun = Some(tun); - self.last_tun_config = config; + fn env(&self) -> Result<JnixEnv<'_>, Error> { + let jni_env = self + .jvm + .attach_current_thread_as_daemon() + .map_err(Error::AttachJvmToThread)?; - Ok(()) - } - value => Err(Error::InvalidMethodResult( - "createTun", - format!("{:?}", value), - )), - } + Ok(JnixEnv::from(jni_env)) } } |
