summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2019-09-11 06:31:56 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2019-09-11 06:31:56 -0300
commitff18ea9d2c9e3d99ea58946cbdd5e1ed87829f8f (patch)
tree2688a051ce9153b5ce9bc23c932ef2a2f5531cd6
parent235506bc6ec9b453716b2f85e4abdb57dd54380d (diff)
parent55199132b8d13adc979bfefaa7ea280e761afca8 (diff)
downloadmullvadvpn-ff18ea9d2c9e3d99ea58946cbdd5e1ed87829f8f.tar.xz
mullvadvpn-ff18ea9d2c9e3d99ea58946cbdd5e1ed87829f8f.zip
Merge branch 'simple-android-firewall'
-rw-r--r--Cargo.lock1
-rw-r--r--mullvad-jni/Cargo.toml1
-rw-r--r--mullvad-jni/src/vpn_service_tun_provider.rs178
-rw-r--r--talpid-core/src/tunnel/mod.rs4
-rw-r--r--talpid-core/src/tunnel/tun_provider/mod.rs14
-rw-r--r--talpid-core/src/tunnel/tun_provider/stub.rs12
-rw-r--r--talpid-core/src/tunnel/tun_provider/unix.rs2
-rw-r--r--talpid-core/src/tunnel/wireguard/mod.rs2
-rw-r--r--talpid-core/src/tunnel/wireguard/wireguard_go.rs4
-rw-r--r--talpid-core/src/tunnel_state_machine/blocked_state.rs18
-rw-r--r--talpid-core/src/tunnel_state_machine/connecting_state.rs6
-rw-r--r--talpid-core/src/tunnel_state_machine/disconnected_state.rs3
12 files changed, 181 insertions, 64 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 17330baa87..856ee14fec 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1237,6 +1237,7 @@ dependencies = [
"jni 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-client-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log-panics 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mullvad-daemon 2019.7.0",
diff --git a/mullvad-jni/Cargo.toml b/mullvad-jni/Cargo.toml
index 21b1d15ba3..2f0884f29d 100644
--- a/mullvad-jni/Cargo.toml
+++ b/mullvad-jni/Cargo.toml
@@ -16,6 +16,7 @@ ipnetwork = "0.14"
jni = "0.12"
jsonrpc-client-core = "0.5"
lazy_static = "1"
+libc = "0.2"
log = "0.4"
log-panics = "2"
parking_lot = "0.8"
diff --git a/mullvad-jni/src/vpn_service_tun_provider.rs b/mullvad-jni/src/vpn_service_tun_provider.rs
index 3d422d83e0..bcc52c53bf 100644
--- a/mullvad-jni/src/vpn_service_tun_provider.rs
+++ b/mullvad-jni/src/vpn_service_tun_provider.rs
@@ -1,10 +1,16 @@
use crate::{get_class, into_java::IntoJava};
+use ipnetwork::IpNetwork;
use jni::{
objects::{GlobalRef, JObject, JValue},
signature::{JavaType, Primitive},
JNIEnv, JavaVM,
};
-use std::os::unix::io::{AsRawFd, RawFd};
+use std::{
+ fs::File,
+ io,
+ net::{IpAddr, Ipv4Addr, Ipv6Addr},
+ os::unix::io::{AsRawFd, FromRawFd, RawFd},
+};
use talpid_core::tunnel::tun_provider::{Tun, TunConfig, TunProvider};
use talpid_types::BoxedError;
@@ -18,19 +24,29 @@ pub enum Error {
#[error(display = "Failed to allow socket to bypass tunnel")]
Bypass,
- #[error(display = "Failed to call Java method {}", _0)]
+ #[error(display = "Failed to call Java method MullvadVpnService.{}", _0)]
CallMethod(&'static str, #[error(cause)] jni::errors::Error),
+ #[error(display = "Failed to create Java VM handle clone")]
+ CloneJavaVm(#[error(cause)] jni::errors::Error),
+
#[error(display = "Failed to create global reference to MullvadVpnService instance")]
CreateGlobalReference(#[error(cause)] jni::errors::Error),
- #[error(display = "Failed to find {} method", _0)]
+ #[error(display = "Failed to duplicate tunnel file descriptor")]
+ DuplicateTunFd(#[error(cause)] io::Error),
+
+ #[error(display = "Failed to find MullvadVpnService.{} method", _0)]
FindMethod(&'static str, #[error(cause)] jni::errors::Error),
#[error(display = "Failed to get Java VM instance")]
GetJvmInstance(#[error(cause)] jni::errors::Error),
- #[error(display = "Received an invalid result from {}: {}", _0, _1)]
+ #[error(
+ display = "Received an invalid result from MullvadVpnService.{}: {}",
+ _0,
+ _1
+ )]
InvalidMethodResult(&'static str, String),
}
@@ -39,6 +55,8 @@ pub struct VpnServiceTunProvider {
jvm: JavaVM,
class: GlobalRef,
object: GlobalRef,
+ active_tun: Option<File>,
+ last_tun_config: TunConfig,
}
impl VpnServiceTunProvider {
@@ -50,51 +68,113 @@ impl VpnServiceTunProvider {
.new_global_ref(*mullvad_vpn_service)
.map_err(Error::CreateGlobalReference)?;
- Ok(VpnServiceTunProvider { jvm, class, object })
+ // Initial configuration simply intercepts all packets. The only field that matters is
+ // `routes`, because it determines what must enter the tunnel. All other fields contain
+ // stub values.
+ let initial_tun_config = TunConfig {
+ addresses: vec![IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))],
+ dns_servers: Vec::new(),
+ 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"),
+ ],
+ mtu: 1380,
+ };
+
+ Ok(VpnServiceTunProvider {
+ jvm,
+ class,
+ object,
+ active_tun: None,
+ last_tun_config: initial_tun_config,
+ })
+ }
+
+ fn duplicate_tun(tun: &File) -> Result<RawFd, Error> {
+ let tun_fd = unsafe { libc::dup(tun.as_raw_fd()) };
+
+ if tun_fd >= 0 {
+ Ok(tun_fd)
+ } else {
+ Err(Error::DuplicateTunFd(io::Error::last_os_error()))
+ }
+ }
+
+ fn prepare_tun(&mut self, config: TunConfig) -> Result<&File, Error> {
+ if self.active_tun.is_none() || self.last_tun_config != config {
+ let env = self
+ .jvm
+ .attach_current_thread()
+ .map_err(Error::AttachJvmToThread)?;
+ let create_tun_method = env
+ .get_method_id(
+ &self.class,
+ "createTun",
+ "(Lnet/mullvad/mullvadvpn/model/TunConfig;)I",
+ )
+ .map_err(|cause| Error::FindMethod("createTun", cause))?;
+
+ let result = env
+ .call_method_unchecked(
+ self.object.as_obj(),
+ create_tun_method,
+ JavaType::Primitive(Primitive::Int),
+ &[JValue::Object(config.clone().into_java(&env))],
+ )
+ .map_err(|cause| Error::CallMethod("createTun", cause))?;
+
+ match result {
+ JValue::Int(fd) => {
+ let tun = unsafe { File::from_raw_fd(fd) };
+
+ self.active_tun = Some(tun);
+ self.last_tun_config = config;
+ }
+ value => {
+ return Err(Error::InvalidMethodResult(
+ "createTun",
+ format!("{:?}", value),
+ ))
+ }
+ }
+ }
+
+ Ok(self
+ .active_tun
+ .as_ref()
+ .expect("Tunnel should be configured"))
}
}
impl TunProvider for VpnServiceTunProvider {
- fn create_tun(&self, config: TunConfig) -> Result<Box<dyn Tun>, BoxedError> {
- let env = self
- .jvm
- .attach_current_thread()
- .map_err(|cause| BoxedError::new(Error::AttachJvmToThread(cause)))?;
- let create_tun_method = env
- .get_method_id(
- &self.class,
- "createTun",
- "(Lnet/mullvad/mullvadvpn/model/TunConfig;)I",
- )
- .map_err(|cause| {
- BoxedError::new(Error::FindMethod("MullvadVpnService.createTun", cause))
- })?;
+ fn get_tun(&mut self, config: TunConfig) -> Result<Box<dyn Tun>, BoxedError> {
+ let tun = self.prepare_tun(config).map_err(BoxedError::new)?;
+ let tun_fd = Self::duplicate_tun(tun).map_err(BoxedError::new)?;
- let result = env
- .call_method_unchecked(
- self.object.as_obj(),
- create_tun_method,
- JavaType::Primitive(Primitive::Int),
- &[JValue::Object(config.into_java(&env))],
- )
- .map_err(|cause| {
- BoxedError::new(Error::CallMethod("MullvadVpnService.createTun", cause))
- })?;
+ let jvm = unsafe { JavaVM::from_raw(self.jvm.get_java_vm_pointer()) }
+ .map_err(|cause| BoxedError::new(Error::CloneJavaVm(cause)))?;
- match result {
- JValue::Int(fd) => Ok(Box::new(VpnServiceTun {
- tunnel: fd,
- jvm: env
- .get_java_vm()
- .map_err(|cause| BoxedError::new(Error::GetJvmInstance(cause)))?,
- class: self.class.clone(),
- object: self.object.clone(),
- })),
- value => Err(BoxedError::new(Error::InvalidMethodResult(
- "MullvadVpnService.createTun",
- format!("{:?}", value),
- ))),
+ Ok(Box::new(VpnServiceTun {
+ tunnel: tun_fd,
+ jvm,
+ class: self.class.clone(),
+ object: self.object.clone(),
+ }))
+ }
+
+ fn create_tun_if_closed(&mut self) -> Result<(), BoxedError> {
+ if self.active_tun.is_none() {
+ self.prepare_tun(self.last_tun_config.clone())
+ .map_err(BoxedError::new)?;
}
+
+ Ok(())
+ }
+
+ fn close_tun(&mut self) {
+ self.active_tun = None;
}
}
@@ -121,11 +201,9 @@ impl Tun for VpnServiceTun {
.jvm
.attach_current_thread()
.map_err(|cause| BoxedError::new(Error::AttachJvmToThread(cause)))?;
- let create_tun_method =
- env.get_method_id(&self.class, "bypass", "(I)Z")
- .map_err(|cause| {
- BoxedError::new(Error::FindMethod("MullvadVpnService.bypass", cause))
- })?;
+ let create_tun_method = env
+ .get_method_id(&self.class, "bypass", "(I)Z")
+ .map_err(|cause| BoxedError::new(Error::FindMethod("bypass", cause)))?;
let result = env
.call_method_unchecked(
@@ -134,15 +212,13 @@ impl Tun for VpnServiceTun {
JavaType::Primitive(Primitive::Boolean),
&[JValue::Int(socket)],
)
- .map_err(|cause| {
- BoxedError::new(Error::CallMethod("MullvadVpnService.bypass", cause))
- })?;
+ .map_err(|cause| BoxedError::new(Error::CallMethod("bypass", cause)))?;
match result {
JValue::Bool(0) => Err(BoxedError::new(Error::Bypass)),
JValue::Bool(_) => Ok(()),
value => Err(BoxedError::new(Error::InvalidMethodResult(
- "MullvadVpnService.bypass",
+ "bypass",
format!("{:?}", value),
))),
}
diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs
index 9014ee16ab..8fe2f7eb66 100644
--- a/talpid-core/src/tunnel/mod.rs
+++ b/talpid-core/src/tunnel/mod.rs
@@ -145,7 +145,7 @@ impl TunnelMonitor {
log_dir: &Option<PathBuf>,
resource_dir: &Path,
on_event: L,
- tun_provider: &dyn TunProvider,
+ tun_provider: &mut dyn TunProvider,
) -> Result<Self>
where
L: Fn(TunnelEvent) + Send + Clone + Sync + 'static,
@@ -175,7 +175,7 @@ impl TunnelMonitor {
params: &wireguard_types::TunnelParameters,
log: Option<PathBuf>,
on_event: L,
- tun_provider: &dyn TunProvider,
+ tun_provider: &mut dyn TunProvider,
) -> Result<Self>
where
L: Fn(TunnelEvent) + Send + Sync + Clone + 'static,
diff --git a/talpid-core/src/tunnel/tun_provider/mod.rs b/talpid-core/src/tunnel/tun_provider/mod.rs
index d9578ebbd1..3cac459c51 100644
--- a/talpid-core/src/tunnel/tun_provider/mod.rs
+++ b/talpid-core/src/tunnel/tun_provider/mod.rs
@@ -49,12 +49,20 @@ pub trait Tun: Send {
/// Factory of tunnel devices.
pub trait TunProvider: Send + 'static {
- /// Create a tunnel device using the provided configuration.
- fn create_tun(&self, config: TunConfig) -> Result<Box<dyn Tun>, BoxedError>;
+ /// Retrieve a tunnel device with the provided configuration.
+ fn get_tun(&mut self, config: TunConfig) -> Result<Box<dyn Tun>, BoxedError>;
+
+ /// Open a tunnel device using the previous or the default configuration.
+ #[cfg(target_os = "android")]
+ fn create_tun_if_closed(&mut self) -> Result<(), BoxedError>;
+
+ /// Close currently active tunnel device.
+ #[cfg(target_os = "android")]
+ fn close_tun(&mut self);
}
/// Configuration for creating a tunnel device.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TunConfig {
/// IP addresses for the tunnel interface.
pub addresses: Vec<IpAddr>,
diff --git a/talpid-core/src/tunnel/tun_provider/stub.rs b/talpid-core/src/tunnel/tun_provider/stub.rs
index 0b94b426bf..be985c8045 100644
--- a/talpid-core/src/tunnel/tun_provider/stub.rs
+++ b/talpid-core/src/tunnel/tun_provider/stub.rs
@@ -11,7 +11,17 @@ impl Default for StubTunProvider {
}
impl TunProvider for StubTunProvider {
- fn create_tun(&self, _: TunConfig) -> Result<Box<dyn Tun>, BoxedError> {
+ fn get_tun(&mut self, _: TunConfig) -> Result<Box<dyn Tun>, BoxedError> {
+ unimplemented!();
+ }
+
+ #[cfg(target_os = "android")]
+ fn create_tun_if_closed(&mut self) -> Result<(), BoxedError> {
+ unimplemented!();
+ }
+
+ #[cfg(target_os = "android")]
+ fn close_tun(&mut self) {
unimplemented!();
}
}
diff --git a/talpid-core/src/tunnel/tun_provider/unix.rs b/talpid-core/src/tunnel/tun_provider/unix.rs
index c0715a81bd..461e49a3b6 100644
--- a/talpid-core/src/tunnel/tun_provider/unix.rs
+++ b/talpid-core/src/tunnel/tun_provider/unix.rs
@@ -29,7 +29,7 @@ impl Default for UnixTunProvider {
}
impl TunProvider for UnixTunProvider {
- fn create_tun(&self, config: TunConfig) -> Result<Box<dyn Tun>, BoxedError> {
+ fn get_tun(&mut self, config: TunConfig) -> Result<Box<dyn Tun>, BoxedError> {
let mut tunnel_device = TunnelDevice::new()
.map_err(|cause| BoxedError::new(Error::CreateTunnelDevice(cause)))?;
diff --git a/talpid-core/src/tunnel/wireguard/mod.rs b/talpid-core/src/tunnel/wireguard/mod.rs
index 440bd1ae61..71ef1a83b3 100644
--- a/talpid-core/src/tunnel/wireguard/mod.rs
+++ b/talpid-core/src/tunnel/wireguard/mod.rs
@@ -82,7 +82,7 @@ impl WireguardMonitor {
config: &Config,
log_path: Option<&Path>,
on_event: F,
- tun_provider: &dyn TunProvider,
+ tun_provider: &mut dyn TunProvider,
) -> Result<WireguardMonitor> {
let tunnel = Box::new(WgGoTunnel::start_tunnel(
&config,
diff --git a/talpid-core/src/tunnel/wireguard/wireguard_go.rs b/talpid-core/src/tunnel/wireguard/wireguard_go.rs
index 38a0c83218..a771bfae4a 100644
--- a/talpid-core/src/tunnel/wireguard/wireguard_go.rs
+++ b/talpid-core/src/tunnel/wireguard/wireguard_go.rs
@@ -18,12 +18,12 @@ impl WgGoTunnel {
pub fn start_tunnel(
config: &Config,
log_path: Option<&Path>,
- tun_provider: &dyn TunProvider,
+ tun_provider: &mut dyn TunProvider,
routes: impl Iterator<Item = IpNetwork>,
) -> Result<Self> {
#[cfg_attr(not(target_os = "android"), allow(unused_mut))]
let mut tunnel_device = tun_provider
- .create_tun(Self::create_tunnel_config(config, routes))
+ .get_tun(Self::create_tunnel_config(config, routes))
.map_err(Error::SetupTunnelDeviceError)?;
let interface_name: String = tunnel_device.interface_name().to_string();
diff --git a/talpid-core/src/tunnel_state_machine/blocked_state.rs b/talpid-core/src/tunnel_state_machine/blocked_state.rs
index 2387d4e922..bdf054ab2c 100644
--- a/talpid-core/src/tunnel_state_machine/blocked_state.rs
+++ b/talpid-core/src/tunnel_state_machine/blocked_state.rs
@@ -30,6 +30,22 @@ impl BlockedState {
}
}
}
+
+ #[cfg(target_os = "android")]
+ fn create_blocking_tun(shared_values: &mut SharedTunnelStateValues) -> Option<BlockReason> {
+ match shared_values.tun_provider.create_tun_if_closed() {
+ Ok(()) => None,
+ Err(error) => {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg(
+ "Failed to open tunnel adapter to drop packets for blocked state"
+ )
+ );
+ Some(BlockReason::SetFirewallPolicyError)
+ }
+ }
+ }
}
impl TunnelState for BlockedState {
@@ -40,6 +56,8 @@ impl TunnelState for BlockedState {
block_reason: Self::Bootstrap,
) -> (TunnelStateWrapper, TunnelStateTransition) {
let block_reason = Self::set_firewall_policy(shared_values).unwrap_or_else(|| block_reason);
+ #[cfg(target_os = "android")]
+ let block_reason = Self::create_blocking_tun(shared_values).unwrap_or_else(|| block_reason);
(
TunnelStateWrapper::from(BlockedState {
diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs
index 2c8b112537..74494241db 100644
--- a/talpid-core/src/tunnel_state_machine/connecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs
@@ -15,7 +15,7 @@ use futures::{
};
use log::{debug, error, info, trace, warn};
use std::{
- borrow::Borrow,
+ borrow::BorrowMut,
net::IpAddr,
path::{Path, PathBuf},
thread,
@@ -64,7 +64,7 @@ impl ConnectingState {
parameters: TunnelParameters,
log_dir: &Option<PathBuf>,
resource_dir: &Path,
- tun_provider: &dyn TunProvider,
+ tun_provider: &mut dyn TunProvider,
retry_attempt: u32,
) -> crate::tunnel::Result<Self> {
let (event_tx, event_rx) = mpsc::unbounded();
@@ -350,7 +350,7 @@ impl TunnelState for ConnectingState {
tunnel_parameters,
&shared_values.log_dir,
&shared_values.resource_dir,
- shared_values.tun_provider.borrow(),
+ shared_values.tun_provider.borrow_mut(),
retry_attempt,
) {
Ok(connecting_state) => {
diff --git a/talpid-core/src/tunnel_state_machine/disconnected_state.rs b/talpid-core/src/tunnel_state_machine/disconnected_state.rs
index 619efb2c10..6b95548ddd 100644
--- a/talpid-core/src/tunnel_state_machine/disconnected_state.rs
+++ b/talpid-core/src/tunnel_state_machine/disconnected_state.rs
@@ -40,6 +40,9 @@ impl TunnelState for DisconnectedState {
_: Self::Bootstrap,
) -> (TunnelStateWrapper, TunnelStateTransition) {
Self::set_firewall_policy(shared_values);
+ #[cfg(target_os = "android")]
+ shared_values.tun_provider.close_tun();
+
(
TunnelStateWrapper::from(DisconnectedState),
TunnelStateTransition::Disconnected,