diff --git a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt index 75983aa61423..b5bbcba79320 100644 --- a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt +++ b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt @@ -31,7 +31,7 @@ open class TalpidVpnService : LifecycleVpnService() { private val tunIsOpen get() = activeTunStatus?.isOpen ?: false - private var currentTunConfig = defaultTunConfig() + private var currentTunConfig: TunConfig? = null private var attemptsWithSameTun = 0 // Used by JNI @@ -156,8 +156,6 @@ open class TalpidVpnService : LifecycleVpnService() { } } - private external fun defaultTunConfig(): TunConfig - private external fun waitForTunnelUp(tunFd: Int, isIpv6Enabled: Boolean) companion object { diff --git a/mullvad-jni/src/talpid_vpn_service.rs b/mullvad-jni/src/talpid_vpn_service.rs index d54e1ad671b1..71873b34792a 100644 --- a/mullvad-jni/src/talpid_vpn_service.rs +++ b/mullvad-jni/src/talpid_vpn_service.rs @@ -1,11 +1,8 @@ use ipnetwork::IpNetwork; -use jnix::{ - jni::{ - objects::JObject, - sys::{jboolean, jint, JNI_FALSE}, - JNIEnv, - }, - IntoJava, JnixEnv, +use jnix::jni::{ + objects::JObject, + sys::{jboolean, jint, JNI_FALSE}, + JNIEnv, }; use nix::sys::{ select::{pselect, FdSet}, @@ -18,7 +15,6 @@ use std::{ os::unix::io::RawFd, time::{Duration, Instant}, }; -use talpid_tunnel::tun_provider::TunConfig; use talpid_types::ErrorExt; #[derive(Debug, thiserror::Error)] @@ -33,17 +29,6 @@ enum Error { TunnelDeviceTimeout, } -#[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( diff --git a/talpid-tunnel/src/tun_provider/android/mod.rs b/talpid-tunnel/src/tun_provider/android/mod.rs index 7ae4f94baf15..6b141565349f 100644 --- a/talpid-tunnel/src/tun_provider/android/mod.rs +++ b/talpid-tunnel/src/tun_provider/android/mod.rs @@ -54,12 +54,11 @@ pub struct AndroidTunProvider { jvm: Arc, class: GlobalRef, object: GlobalRef, - last_tun_config: TunConfig, + last_tun_config: (TunConfig, bool), allow_lan: bool, - blocking: bool, custom_dns_servers: Option>, allowed_lan_networks: Vec, - excluded_apps: Vec, + excluded_packages: Vec, } impl AndroidTunProvider { @@ -69,7 +68,7 @@ impl AndroidTunProvider { allow_lan: bool, custom_dns_servers: Option>, allowed_lan_networks: Vec, - excluded_apps: Vec, + excluded_packages: Vec, ) -> Self { let env = JnixEnv::from( context @@ -83,12 +82,11 @@ impl AndroidTunProvider { jvm: context.jvm, class: talpid_vpn_service_class, object: context.vpn_service, - last_tun_config: TunConfig::default(), + last_tun_config: (TunConfig::default(), false), allow_lan, - blocking: false, custom_dns_servers, allowed_lan_networks, - excluded_apps, + excluded_packages, } } @@ -113,9 +111,9 @@ impl AndroidTunProvider { /// Update the set of excluded paths (split tunnel apps) for the tunnel provider. /// This will cause any pre-existing tunnel to be recreated if necessary. See /// [`AndroidTunProvider::recreate_tun_if_open()`] for details. - pub fn set_exclude_apps(&mut self, excluded_apps: Vec) -> Result<(), Error> { - if self.excluded_apps != excluded_apps { - self.excluded_apps = excluded_apps; + pub fn set_exclude_apps(&mut self, excluded_packages: Vec) -> Result<(), Error> { + if self.excluded_packages != excluded_packages { + self.excluded_packages = excluded_packages; self.recreate_tun_if_open()?; } Ok(()) @@ -123,17 +121,24 @@ impl AndroidTunProvider { /// Retrieve a tunnel device with the provided configuration. Custom DNS and LAN routes are /// appended to the provided config. - pub fn get_tun(&mut self, mut config: TunConfig) -> Result { - self.prepare_tun_config(&mut config, false); - self.get_tun_inner(config) + pub fn get_tun(&mut self, config: TunConfig) -> Result { + let original_config = config; + let config = VpnServiceConfig::new( + original_config.clone(), + &self.allowed_lan_networks, + self.allow_lan, + self.custom_dns_servers.clone(), + self.excluded_packages.clone(), + ); + let tun = self.get_tun_inner(config)?; + self.last_tun_config = (original_config, false); + Ok(tun) } /// Retrieve a tunnel device with the provided configuration. - fn get_tun_inner(&mut self, config: TunConfig) -> Result { + fn get_tun_inner(&self, config: VpnServiceConfig) -> Result { 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)?; @@ -151,9 +156,17 @@ impl AndroidTunProvider { /// Will open a new tunnel if there is already an active tunnel. The previous tunnel will be /// closed. pub fn create_blocking_tun(&mut self) -> Result<(), Error> { - let mut config = TunConfig::default(); - self.prepare_tun_config(&mut config, true); + let original_config = TunConfig::default(); + let config = VpnServiceConfig::new( + original_config.clone(), + &self.allowed_lan_networks, + self.allow_lan, + // Disable DNS + Some(vec![]), + self.excluded_packages.clone(), + ); let _ = self.get_tun_inner(config)?; + self.last_tun_config = (original_config, true); Ok(()) } @@ -178,7 +191,7 @@ impl AndroidTunProvider { } } - fn get_tun_fd(&mut self, config: TunConfig) -> Result { + fn get_tun_fd(&self, config: VpnServiceConfig) -> Result { let env = self.env()?; let java_config = config.into_java(&env); @@ -196,12 +209,22 @@ impl AndroidTunProvider { } fn recreate_tun_if_open(&mut self) -> Result<(), Error> { - let mut actual_config = self.last_tun_config.clone(); - - self.prepare_tun_config(&mut actual_config, self.blocking); + let (last_tun_config, blocking) = self.last_tun_config.clone(); + + let config = VpnServiceConfig::new( + last_tun_config, + &self.allowed_lan_networks, + self.allow_lan, + if !blocking { + self.custom_dns_servers.clone() + } else { + Some(vec![]) + }, + self.excluded_packages.clone(), + ); let env = self.env()?; - let java_config = actual_config.into_java(&env); + let java_config = config.into_java(&env); let result = self.call_method( "recreateTunIfOpen", @@ -216,65 +239,6 @@ impl AndroidTunProvider { } } - fn prepare_tun_config(&mut self, config: &mut TunConfig, blocking: bool) { - self.blocking = blocking; - self.prepare_tun_config_for_allow_lan(config); - self.prepare_tun_config_for_excluded_apps(config); - if !blocking { - self.prepare_tun_config_for_custom_dns(config); - } - } - - fn prepare_tun_config_for_allow_lan(&self, config: &mut TunConfig) { - if self.allow_lan { - let (required_ipv4_routes, required_ipv6_routes) = config - .required_routes - .iter() - .cloned() - .partition::, _>(|route| route.is_ipv4()); - - let (original_lan_ipv4_networks, original_lan_ipv6_networks) = self - .allowed_lan_networks - .iter() - .cloned() - .partition::, _>(|network| network.is_ipv4()); - - let lan_ipv4_networks = original_lan_ipv4_networks - .into_iter() - .flat_map(|network| network.sub_all(required_ipv4_routes.iter().cloned())) - .collect::>(); - - let lan_ipv6_networks = original_lan_ipv6_networks - .into_iter() - .flat_map(|network| network.sub_all(required_ipv6_routes.iter().cloned())) - .collect::>(); - - let routes = config - .routes - .iter() - .flat_map(|&route| { - if route.is_ipv4() { - route.sub_all(lan_ipv4_networks.iter().cloned()) - } else { - route.sub_all(lan_ipv6_networks.iter().cloned()) - } - }) - .collect(); - - config.routes = routes; - } - } - - fn prepare_tun_config_for_custom_dns(&self, config: &mut TunConfig) { - if let Some(custom_dns_servers) = self.custom_dns_servers.clone() { - config.dns_servers = custom_dns_servers; - } - } - - fn prepare_tun_config_for_excluded_apps(&self, config: &mut TunConfig) { - config.excluded_packages.clone_from(&self.excluded_apps); - } - /// Allow a socket to bypass the tunnel. pub fn bypass(&mut self, socket: RawFd) -> Result<(), Error> { let env = JnixEnv::from( @@ -332,6 +296,122 @@ impl AndroidTunProvider { } } +/// Configuration to use for VpnService +#[derive(Clone, Debug, Eq, PartialEq, IntoJava)] +#[jnix(class_name = "net.mullvad.talpid.model.TunConfig")] +struct VpnServiceConfig { + /// IP addresses for the tunnel interface. + pub addresses: Vec, + + /// IP addresses for the DNS servers to use. + pub dns_servers: Vec, + + /// Routes to configure for the tunnel. + pub routes: Vec, + + /// App packages that should be excluded from the tunnel. + pub excluded_packages: Vec, + + /// Maximum Transmission Unit in the tunnel. + #[jnix(map = "|mtu| mtu as i32")] + pub mtu: u16, +} + +impl VpnServiceConfig { + pub fn new( + tun_config: TunConfig, + allowed_lan_networks: &[IpNetwork], + allow_lan: bool, + dns_servers: Option>, + excluded_packages: Vec, + ) -> VpnServiceConfig { + let dns_servers = Self::resolve_dns_servers(&tun_config, dns_servers); + let routes = Self::resolve_routes(&tun_config, allowed_lan_networks, allow_lan); + + VpnServiceConfig { + addresses: tun_config.addresses, + dns_servers, + routes, + excluded_packages, + mtu: tun_config.mtu, + } + } + + /// Return a list of custom DNS servers. If not specified, gateway addresses are used for DNS. + /// Note that `Some(vec![])` is different from `None`. `Some(vec![])` disables DNS. + fn resolve_dns_servers(config: &TunConfig, custom_dns: Option>) -> Vec { + custom_dns.unwrap_or_else(|| config.gateway_ips()) + } + + /// Potentially subtract LAN nets from the VPN service routes, excepting gateways. + /// This prevents LAN traffic from going in the tunnel. + fn resolve_routes( + config: &TunConfig, + allowed_lan_networks: &[IpNetwork], + allow_lan: bool, + ) -> Vec { + if !allow_lan { + return config + .routes + .iter() + .cloned() + .map(InetNetwork::from) + .collect(); + } + + let required_ipv4_routes = vec![IpNetwork::from(IpAddr::from(config.ipv4_gateway))]; + let required_ipv6_routes = config + .ipv6_gateway + .map(|addr| IpNetwork::from(IpAddr::from(addr))) + .into_iter() + .collect::>(); + + let (original_lan_ipv4_networks, original_lan_ipv6_networks) = allowed_lan_networks + .iter() + .cloned() + .partition::, _>(|network| network.is_ipv4()); + + let lan_ipv4_networks = original_lan_ipv4_networks + .into_iter() + .flat_map(|network| network.sub_all(required_ipv4_routes.clone())) + .collect::>(); + + let lan_ipv6_networks = original_lan_ipv6_networks + .into_iter() + .flat_map(|network| network.sub_all(required_ipv6_routes.clone())) + .collect::>(); + + config + .routes + .iter() + .flat_map(|&route| { + if route.is_ipv4() { + route.sub_all(lan_ipv4_networks.clone()) + } else { + route.sub_all(lan_ipv6_networks.clone()) + } + }) + .map(InetNetwork::from) + .collect() + } +} + +#[derive(Clone, Debug, Eq, PartialEq, IntoJava)] +#[jnix(package = "net.mullvad.talpid.model")] +struct InetNetwork { + address: IpAddr, + prefix: i16, +} + +impl From for InetNetwork { + fn from(ip_network: IpNetwork) -> Self { + InetNetwork { + address: ip_network.ip(), + prefix: ip_network.prefix() as i16, + } + } +} + /// Handle to a tunnel device on Android. pub struct VpnServiceTun { tunnel: RawFd, @@ -386,15 +466,14 @@ impl Default for TunConfig { // stub values. TunConfig { addresses: vec![IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))], - dns_servers: Vec::new(), + ipv4_gateway: Ipv4Addr::new(10, 64, 0, 1), + ipv6_gateway: None, routes: vec![ IpNetwork::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0) .expect("Invalid IP network prefix for IPv4 address"), IpNetwork::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), 0) .expect("Invalid IP network prefix for IPv6 address"), ], - required_routes: vec![], - excluded_packages: vec![], mtu: 1380, } } diff --git a/talpid-tunnel/src/tun_provider/mod.rs b/talpid-tunnel/src/tun_provider/mod.rs index 3214dd1fde0c..66ab43c3dfae 100644 --- a/talpid-tunnel/src/tun_provider/mod.rs +++ b/talpid-tunnel/src/tun_provider/mod.rs @@ -1,8 +1,6 @@ use cfg_if::cfg_if; use ipnetwork::IpNetwork; -#[cfg(target_os = "android")] -use jnix::IntoJava; -use std::net::IpAddr; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; cfg_if! { if #[cfg(target_os = "android")] { @@ -32,50 +30,30 @@ cfg_if! { /// Configuration for creating a tunnel device. #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(target_os = "android", derive(IntoJava))] -#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.talpid.model"))] pub struct TunConfig { /// IP addresses for the tunnel interface. pub addresses: Vec, - /// IP addresses for the DNS servers to use. - pub dns_servers: Vec, + /// IPv4 address of the VPN server, and the default IPv4 DNS resolver. + pub ipv4_gateway: Ipv4Addr, + + /// IPv6 address of the VPN server, and the default IPv6 DNS resolver. + pub ipv6_gateway: Option, /// Routes to configure for the tunnel. - #[cfg_attr( - target_os = "android", - jnix(map = "|networks| networks.into_iter().map(InetNetwork::from).collect::>()") - )] pub routes: Vec, - /// Routes that are required to be configured for the tunnel. - #[cfg(target_os = "android")] - #[jnix(skip)] - pub required_routes: Vec, - - /// App packages that should be excluded from the tunnel. - #[cfg(target_os = "android")] - pub excluded_packages: Vec, - - /// Maximum Transmission Unit in the tunnel. - #[cfg_attr(target_os = "android", jnix(map = "|mtu| mtu as i32"))] + /// MTU of the tunnel interface. pub mtu: u16, } -#[cfg(target_os = "android")] -#[derive(IntoJava)] -#[jnix(package = "net.mullvad.talpid.model")] -struct InetNetwork { - address: IpAddr, - prefix: i16, -} - -#[cfg(target_os = "android")] -impl From for InetNetwork { - fn from(ip_network: IpNetwork) -> Self { - InetNetwork { - address: ip_network.ip(), - prefix: ip_network.prefix() as i16, +impl TunConfig { + /// Return a copy of all gateway addresses + pub fn gateway_ips(&self) -> Vec { + let mut servers = vec![self.ipv4_gateway.into()]; + if let Some(gateway) = self.ipv6_gateway { + servers.push(gateway.into()); } + servers } } diff --git a/talpid-wireguard/src/wireguard_go/mod.rs b/talpid-wireguard/src/wireguard_go/mod.rs index 851734bc9d54..3f3ed97d6457 100644 --- a/talpid-wireguard/src/wireguard_go/mod.rs +++ b/talpid-wireguard/src/wireguard_go/mod.rs @@ -5,7 +5,6 @@ use once_cell::sync::OnceCell; use std::{ffi::CString, fs, path::PathBuf}; use std::{ future::Future, - net::IpAddr, os::unix::io::{AsRawFd, RawFd}, path::Path, pin::Pin, @@ -108,35 +107,6 @@ impl WgGoTunnel { }) } - fn create_tunnel_config( - config: &Config, - routes: impl Iterator, - ) -> TunConfig { - let mut dns_servers = vec![IpAddr::V4(config.ipv4_gateway)]; - dns_servers.extend(config.ipv6_gateway.map(IpAddr::V6)); - - TunConfig { - addresses: config.tunnel.addresses.clone(), - dns_servers, - routes: routes.collect(), - #[cfg(target_os = "android")] - required_routes: Self::create_required_routes(config), - mtu: config.mtu, - } - } - - #[cfg(target_os = "android")] - fn create_required_routes(config: &Config) -> Vec { - let mut required_routes = vec![IpNetwork::new(IpAddr::V4(config.ipv4_gateway), 32) - .expect("Invalid IPv4 network prefix")]; - - required_routes.extend(config.ipv6_gateway.map(|address| { - IpNetwork::new(IpAddr::V6(address), 128).expect("Invalid IPv6 network prefix") - })); - - required_routes - } - #[cfg(target_os = "android")] fn bypass_tunnel_sockets( handle: &wireguard_go_rs::Tunnel, @@ -159,10 +129,13 @@ impl WgGoTunnel { let mut last_error = None; let mut tun_provider = tun_provider.lock().unwrap(); - let tunnel_config = Self::create_tunnel_config( - config, - routes, - ); + let tunnel_config = TunConfig { + addresses: config.tunnel.addresses.clone(), + ipv4_gateway: config.ipv4_gateway, + ipv6_gateway: config.ipv6_gateway, + routes: routes.collect(), + mtu: config.mtu, + }; for _ in 1..=MAX_PREPARE_TUN_ATTEMPTS { let tunnel_device = tun_provider