diff options
| author | David Lönnhager <david.l@mullvad.net> | 2020-02-28 18:29:35 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2020-06-02 10:05:02 +0200 |
| commit | 2b2f63957a2cfd03a716ef50887a4b268cc6ddda (patch) | |
| tree | 944a00f0f9409471840cc3de63a69a6f66e1a60a | |
| parent | ac016280138bfdfb0d9bc9acb43ea88612d425eb (diff) | |
| download | mullvadvpn-2b2f63957a2cfd03a716ef50887a4b268cc6ddda.tar.xz mullvadvpn-2b2f63957a2cfd03a716ef50887a4b268cc6ddda.zip | |
Route DNS requests through tunnel for PIDs in split-tunnel cgroup
| -rw-r--r-- | talpid-core/src/split.rs | 55 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connected_state.rs | 18 |
2 files changed, 68 insertions, 5 deletions
diff --git a/talpid-core/src/split.rs b/talpid-core/src/split.rs index 04a57c2034..cab42fd265 100644 --- a/talpid-core/src/split.rs +++ b/talpid-core/src/split.rs @@ -2,6 +2,7 @@ use regex::Regex; use std::{ fs, io::{self, BufRead, BufReader, Write}, + net::IpAddr, path::Path, process::Command, }; @@ -45,6 +46,14 @@ pub enum Error { /// Unable to read cgroup.procs. #[error(display = "Unable to obtain PIDs from cgroup.procs")] ListCGroupPids(#[error(source)] io::Error), + + /// Unable to add setup DNS routing. + #[error(display = "Failed to add routing table DNS rules")] + SetDns(#[error(source)] io::Error), + + /// Unable to flush routing table. + #[error(display = "Failed to clear routing table DNS rules")] + FlushDns(#[error(source)] io::Error), } /// Route PID-associated packets through the physical interface. @@ -75,6 +84,52 @@ pub fn route_marked_packets() -> Result<(), Error> { cmd.output().map(|_| ()).map_err(Error::RoutingTableSetup) } +/// Route DNS requests through the tunnel interface. +pub fn route_dns(tunnel_alias: &str, dns_servers: &[IpAddr]) -> Result<(), Error> { + // TODO: IPv6 + + let mut cmd = Command::new("ip"); + cmd.args(&["-4", "route", "flush", "table", ROUTING_TABLE_NAME]); + + log::trace!("running cmd - {:?}", &cmd); + cmd.output().map_err(Error::SetDns)?; + + for server in dns_servers { + if let IpAddr::V4(addr) = server { + let addr = addr.to_string(); + + let mut cmd = Command::new("ip"); + cmd.args(&[ + "-4", + "route", + "add", + &addr, + "via", + &addr, + "dev", + tunnel_alias, + "table", + ROUTING_TABLE_NAME, + ]); + + log::trace!("running cmd - {:?}", &cmd); + cmd.output().map_err(Error::SetDns)?; + } + } + + Ok(()) +} + +/// Reset DNS rules. +pub fn flush_dns() -> Result<(), Error> { + // For now, simply flush it + let mut cmd = Command::new("ip"); + cmd.args(&["-4", "route", "flush", "table", ROUTING_TABLE_NAME]); + + log::trace!("running cmd - {:?}", &cmd); + cmd.output().map(|_| ()).map_err(Error::FlushDns) +} + /// Set up policy-based routing for marked packets. pub fn initialize_routing_table() -> Result<(), Error> { // TODO: ensure the ID does not conflict with that of another table diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs index deb7e265a4..e936499f47 100644 --- a/talpid-core/src/tunnel_state_machine/connected_state.rs +++ b/talpid-core/src/tunnel_state_machine/connected_state.rs @@ -4,6 +4,7 @@ use super::{ }; use crate::{ firewall::FirewallPolicy, + split, tunnel::{CloseHandle, TunnelEvent, TunnelMetadata}, }; use futures01::{ @@ -13,7 +14,7 @@ use futures01::{ use talpid_types::{ net::{Endpoint, TunnelParameters}, tunnel::ErrorStateCause, - ErrorExt, + BoxedError, ErrorExt, }; pub struct ConnectedStateBootstrap { @@ -69,10 +70,7 @@ impl ConnectedState { } } - fn set_dns( - &self, - shared_values: &mut SharedTunnelStateValues, - ) -> Result<(), crate::dns::Error> { + fn set_dns(&self, shared_values: &mut SharedTunnelStateValues) -> Result<(), BoxedError> { let mut dns_ips = vec![self.metadata.ipv4_gateway.into()]; if let Some(ipv6_gateway) = self.metadata.ipv6_gateway { dns_ips.push(ipv6_gateway.into()); @@ -81,9 +79,19 @@ impl ConnectedState { shared_values .dns_monitor .set(&self.metadata.interface, &dns_ips) + .map_err(BoxedError::new)?; + + split::route_dns(&self.metadata.interface, &dns_ips).map_err(BoxedError::new) } fn reset_dns(shared_values: &mut SharedTunnelStateValues) { + if let Err(error) = split::flush_dns() { + log::error!( + "{}", + error.display_chain_with_msg("Unable to update split-tunnel route") + ); + } + if let Err(error) = shared_values.dns_monitor.reset() { log::error!("{}", error.display_chain_with_msg("Unable to reset DNS")); } |
